@webex/plugin-meetings 3.11.0-next.4 → 3.11.0-next.40
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
|
@@ -115,6 +115,7 @@ describe('plugin-meetings', () => {
|
|
|
115
115
|
});
|
|
116
116
|
|
|
117
117
|
describe('#changeVideoLayout', () => {
|
|
118
|
+
|
|
118
119
|
const locusUrl = 'locusURL';
|
|
119
120
|
const deviceUrl = 'deviceUrl';
|
|
120
121
|
const layoutType = 'Equal';
|
|
@@ -918,4 +919,67 @@ describe('plugin-meetings', () => {
|
|
|
918
919
|
});
|
|
919
920
|
});
|
|
920
921
|
});
|
|
922
|
+
|
|
923
|
+
describe('#fetchDatachannelToken', () => {
|
|
924
|
+
const locusUrl = 'https://locus.example.com/locus/api/v1/loci/123';
|
|
925
|
+
const participantId = 'participant-123';
|
|
926
|
+
|
|
927
|
+
it('sends GET request to regular datachannel token endpoint', async () => {
|
|
928
|
+
await meetingsRequest.fetchDatachannelToken({
|
|
929
|
+
locusUrl,
|
|
930
|
+
requestingParticipantId: participantId,
|
|
931
|
+
isPracticeSession: false,
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
assert.calledOnceWithExactly(locusDeltaRequestSpy, {
|
|
935
|
+
method: 'GET',
|
|
936
|
+
uri: `${locusUrl}/participant/${participantId}/datachannel/token`,
|
|
937
|
+
});
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
it('sends GET request to practice session datachannel token endpoint', async () => {
|
|
941
|
+
await meetingsRequest.fetchDatachannelToken({
|
|
942
|
+
locusUrl,
|
|
943
|
+
requestingParticipantId: participantId,
|
|
944
|
+
isPracticeSession: true,
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
assert.calledOnceWithExactly(locusDeltaRequestSpy, {
|
|
948
|
+
method: 'GET',
|
|
949
|
+
uri: `${locusUrl}/participant/${participantId}/practiceSession/datachannel/token`,
|
|
950
|
+
});
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
it('throws if locusUrl or participantId is missing', async () => {
|
|
954
|
+
await assert.isRejected(
|
|
955
|
+
meetingsRequest.fetchDatachannelToken({
|
|
956
|
+
locusUrl: null,
|
|
957
|
+
requestingParticipantId: participantId,
|
|
958
|
+
}),
|
|
959
|
+
/locusUrl and participantId are required/
|
|
960
|
+
);
|
|
961
|
+
|
|
962
|
+
await assert.isRejected(
|
|
963
|
+
meetingsRequest.fetchDatachannelToken({
|
|
964
|
+
locusUrl,
|
|
965
|
+
requestingParticipantId: null,
|
|
966
|
+
}),
|
|
967
|
+
/locusUrl and participantId are required/
|
|
968
|
+
);
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
it('logs and rethrows error when locusDeltaRequest fails', async () => {
|
|
972
|
+
const error = new Error('network error');
|
|
973
|
+
locusDeltaRequestSpy.restore();
|
|
974
|
+
sinon.stub(meetingsRequest, 'locusDeltaRequest').rejects(error);
|
|
975
|
+
|
|
976
|
+
await assert.isRejected(
|
|
977
|
+
meetingsRequest.fetchDatachannelToken({
|
|
978
|
+
locusUrl,
|
|
979
|
+
requestingParticipantId: participantId,
|
|
980
|
+
}),
|
|
981
|
+
/network error/
|
|
982
|
+
);
|
|
983
|
+
});
|
|
984
|
+
});
|
|
921
985
|
});
|
|
@@ -11,6 +11,7 @@ import MockWebex from '@webex/test-helper-mock-webex';
|
|
|
11
11
|
import * as BrowserDetectionModule from '@webex/plugin-meetings/src/common/browser-detection';
|
|
12
12
|
import PasswordError from '@webex/plugin-meetings/src/common/errors/password-error';
|
|
13
13
|
import CaptchaError from '@webex/plugin-meetings/src/common/errors/captcha-error';
|
|
14
|
+
import {ServerRoles} from '@webex/plugin-meetings/src/member/types';
|
|
14
15
|
|
|
15
16
|
describe('plugin-meetings', () => {
|
|
16
17
|
let webex;
|
|
@@ -61,8 +62,9 @@ describe('plugin-meetings', () => {
|
|
|
61
62
|
meeting.trigger = sinon.stub();
|
|
62
63
|
meeting.webex = webex;
|
|
63
64
|
meeting.webex.internal.newMetrics.callDiagnosticMetrics =
|
|
64
|
-
|
|
65
|
-
meeting.webex.internal.newMetrics.callDiagnosticMetrics.clearEventLimitsForCorrelationId =
|
|
65
|
+
meeting.webex.internal.newMetrics.callDiagnosticMetrics || {};
|
|
66
|
+
meeting.webex.internal.newMetrics.callDiagnosticMetrics.clearEventLimitsForCorrelationId =
|
|
67
|
+
sinon.stub();
|
|
66
68
|
});
|
|
67
69
|
|
|
68
70
|
afterEach(() => {
|
|
@@ -245,7 +247,11 @@ describe('plugin-meetings', () => {
|
|
|
245
247
|
const response = MeetingUtil.updateLocusFromApiResponse(meeting, originalResponse);
|
|
246
248
|
|
|
247
249
|
assert.deepEqual(response, originalResponse);
|
|
248
|
-
assert.calledOnceWithExactly(
|
|
250
|
+
assert.calledOnceWithExactly(
|
|
251
|
+
meeting.locusInfo.handleLocusAPIResponse,
|
|
252
|
+
meeting,
|
|
253
|
+
originalResponse.body
|
|
254
|
+
);
|
|
249
255
|
});
|
|
250
256
|
|
|
251
257
|
it('should handle locus being missing from the response', () => {
|
|
@@ -361,8 +367,8 @@ describe('plugin-meetings', () => {
|
|
|
361
367
|
describe('remoteUpdateAudioVideo', () => {
|
|
362
368
|
it('#Should call meetingRequest.locusMediaRequest with correct parameters and return the full response', async () => {
|
|
363
369
|
const fakeResponse = {
|
|
364
|
-
body: {
|
|
365
|
-
headers: {
|
|
370
|
+
body: {locus: {url: 'locusUrl'}},
|
|
371
|
+
headers: {},
|
|
366
372
|
};
|
|
367
373
|
const meeting = {
|
|
368
374
|
id: 'meeting-id',
|
|
@@ -480,6 +486,11 @@ describe('plugin-meetings', () => {
|
|
|
480
486
|
identifiers: {
|
|
481
487
|
trackingId: 'trackingId',
|
|
482
488
|
},
|
|
489
|
+
eventData: {
|
|
490
|
+
hasMismatchedSocket: false,
|
|
491
|
+
mercurySocketUrl: '',
|
|
492
|
+
deviceSocketUrl: 'ws://example.com',
|
|
493
|
+
},
|
|
483
494
|
},
|
|
484
495
|
options: {
|
|
485
496
|
meetingId: meeting.id,
|
|
@@ -649,21 +660,26 @@ describe('plugin-meetings', () => {
|
|
|
649
660
|
it('should post client event with error when join fails', async () => {
|
|
650
661
|
const joinError = new Error('Join failed');
|
|
651
662
|
meeting.meetingRequest.joinMeeting.rejects(joinError);
|
|
652
|
-
meeting.meetingInfo = {
|
|
663
|
+
meeting.meetingInfo = {meetingLookupUrl: 'test-lookup-url'};
|
|
653
664
|
|
|
654
665
|
try {
|
|
655
666
|
await MeetingUtil.joinMeeting(meeting, {});
|
|
656
667
|
assert.fail('Expected joinMeeting to throw an error');
|
|
657
668
|
} catch (error) {
|
|
658
669
|
assert.equal(error, joinError);
|
|
659
|
-
|
|
670
|
+
|
|
660
671
|
// Verify error client event was submitted
|
|
661
672
|
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
|
662
673
|
name: 'client.locus.join.response',
|
|
663
674
|
payload: {
|
|
664
|
-
identifiers: {
|
|
675
|
+
identifiers: {meetingLookupUrl: 'test-lookup-url'},
|
|
676
|
+
eventData: {
|
|
677
|
+
hasMismatchedSocket: false,
|
|
678
|
+
mercurySocketUrl: '',
|
|
679
|
+
deviceSocketUrl: 'ws://example.com',
|
|
680
|
+
},
|
|
665
681
|
},
|
|
666
|
-
options: {
|
|
682
|
+
options: {meetingId: meeting.id, rawError: joinError},
|
|
667
683
|
});
|
|
668
684
|
}
|
|
669
685
|
});
|
|
@@ -721,7 +737,7 @@ describe('plugin-meetings', () => {
|
|
|
721
737
|
assert.fail('Expected joinMeetingOptions to throw PasswordError');
|
|
722
738
|
} catch (error) {
|
|
723
739
|
assert.instanceOf(error, PasswordError);
|
|
724
|
-
|
|
740
|
+
|
|
725
741
|
// Verify client event was submitted with error details
|
|
726
742
|
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
|
727
743
|
name: 'client.meetinginfo.response',
|
|
@@ -759,7 +775,7 @@ describe('plugin-meetings', () => {
|
|
|
759
775
|
assert.fail('Expected joinMeetingOptions to throw CaptchaError');
|
|
760
776
|
} catch (error) {
|
|
761
777
|
assert.instanceOf(error, CaptchaError);
|
|
762
|
-
|
|
778
|
+
|
|
763
779
|
// Verify client event was submitted with error details
|
|
764
780
|
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
|
765
781
|
name: 'client.meetinginfo.response',
|
|
@@ -921,6 +937,104 @@ describe('plugin-meetings', () => {
|
|
|
921
937
|
});
|
|
922
938
|
});
|
|
923
939
|
|
|
940
|
+
describe('canAttendeeRequestAiAssistantEnabled', () => {
|
|
941
|
+
it('returns false when user is a cohost', () => {
|
|
942
|
+
assert.deepEqual(
|
|
943
|
+
MeetingUtil.canAttendeeRequestAiAssistantEnabled(
|
|
944
|
+
['ATTENDEE_REQUEST_AI_ASSISTANT_ENABLED'],
|
|
945
|
+
[ServerRoles.Cohost]
|
|
946
|
+
),
|
|
947
|
+
false
|
|
948
|
+
);
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
it('returns false when user is a moderator', () => {
|
|
952
|
+
assert.deepEqual(
|
|
953
|
+
MeetingUtil.canAttendeeRequestAiAssistantEnabled(
|
|
954
|
+
['ATTENDEE_REQUEST_AI_ASSISTANT_ENABLED'],
|
|
955
|
+
[ServerRoles.Moderator]
|
|
956
|
+
),
|
|
957
|
+
false
|
|
958
|
+
);
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
it('returns false when user is both cohost and moderator', () => {
|
|
962
|
+
assert.deepEqual(
|
|
963
|
+
MeetingUtil.canAttendeeRequestAiAssistantEnabled(
|
|
964
|
+
['ATTENDEE_REQUEST_AI_ASSISTANT_ENABLED'],
|
|
965
|
+
[ServerRoles.Cohost, ServerRoles.Moderator]
|
|
966
|
+
),
|
|
967
|
+
false
|
|
968
|
+
);
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
it('returns true when user is an attendee and display hint is present', () => {
|
|
972
|
+
assert.deepEqual(
|
|
973
|
+
MeetingUtil.canAttendeeRequestAiAssistantEnabled(
|
|
974
|
+
['ATTENDEE_REQUEST_AI_ASSISTANT_ENABLED'],
|
|
975
|
+
[]
|
|
976
|
+
),
|
|
977
|
+
true
|
|
978
|
+
);
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
it('returns true when user has other roles (not host/cohost) and display hint is present', () => {
|
|
982
|
+
assert.deepEqual(
|
|
983
|
+
MeetingUtil.canAttendeeRequestAiAssistantEnabled(
|
|
984
|
+
['ATTENDEE_REQUEST_AI_ASSISTANT_ENABLED'],
|
|
985
|
+
['SomeOtherRole']
|
|
986
|
+
),
|
|
987
|
+
true
|
|
988
|
+
);
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
it('returns false when user is an attendee but display hint is not present', () => {
|
|
992
|
+
assert.deepEqual(MeetingUtil.canAttendeeRequestAiAssistantEnabled([], []), false);
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
it('returns false when user is an attendee with other display hints but not the AI assistant one', () => {
|
|
996
|
+
assert.deepEqual(
|
|
997
|
+
MeetingUtil.canAttendeeRequestAiAssistantEnabled(['SOME_OTHER_HINT', 'ANOTHER_HINT'], []),
|
|
998
|
+
false
|
|
999
|
+
);
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
it('returns false when host/cohost even if display hint is not present', () => {
|
|
1003
|
+
assert.deepEqual(
|
|
1004
|
+
MeetingUtil.canAttendeeRequestAiAssistantEnabled([], [ServerRoles.Cohost]),
|
|
1005
|
+
false
|
|
1006
|
+
);
|
|
1007
|
+
assert.deepEqual(
|
|
1008
|
+
MeetingUtil.canAttendeeRequestAiAssistantEnabled([], [ServerRoles.Moderator]),
|
|
1009
|
+
false
|
|
1010
|
+
);
|
|
1011
|
+
});
|
|
1012
|
+
});
|
|
1013
|
+
|
|
1014
|
+
describe('attendeeRequestAiAssistantDeclinedAll', () => {
|
|
1015
|
+
it('returns true when display hint is present', () => {
|
|
1016
|
+
assert.isTrue(
|
|
1017
|
+
MeetingUtil.attendeeRequestAiAssistantDeclinedAll([
|
|
1018
|
+
'ATTENDEE_REQUEST_AI_ASSISTANT_DECLINED_ALL',
|
|
1019
|
+
])
|
|
1020
|
+
);
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
it('returns false when display hint is not present', () => {
|
|
1024
|
+
assert.isFalse(MeetingUtil.attendeeRequestAiAssistantDeclinedAll([]));
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
it('returns false when display hint is absent among other hints', () => {
|
|
1028
|
+
assert.isFalse(
|
|
1029
|
+
MeetingUtil.attendeeRequestAiAssistantDeclinedAll(['SOME_OTHER_HINT', 'ANOTHER_HINT'])
|
|
1030
|
+
);
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
it('returns false when called with no arguments', () => {
|
|
1034
|
+
assert.isFalse(MeetingUtil.attendeeRequestAiAssistantDeclinedAll());
|
|
1035
|
+
});
|
|
1036
|
+
});
|
|
1037
|
+
|
|
924
1038
|
describe('bothLeaveAndEndMeetingAvailable', () => {
|
|
925
1039
|
it('works as expected', () => {
|
|
926
1040
|
assert.deepEqual(
|
|
@@ -939,6 +1053,46 @@ describe('plugin-meetings', () => {
|
|
|
939
1053
|
});
|
|
940
1054
|
});
|
|
941
1055
|
|
|
1056
|
+
describe('requireHostEndMeetingBeforeLeave', () => {
|
|
1057
|
+
it('works as expected', () => {
|
|
1058
|
+
assert.deepEqual(
|
|
1059
|
+
MeetingUtil.requireHostEndMeetingBeforeLeave(['REQUIRE_HOST_END_MEETING_BEFORE_LEAVE']),
|
|
1060
|
+
true
|
|
1061
|
+
);
|
|
1062
|
+
assert.deepEqual(
|
|
1063
|
+
MeetingUtil.requireHostEndMeetingBeforeLeave([
|
|
1064
|
+
'LEAVE_TRANSFER_HOST_END_MEETING',
|
|
1065
|
+
'END_MEETING',
|
|
1066
|
+
]),
|
|
1067
|
+
false
|
|
1068
|
+
);
|
|
1069
|
+
assert.deepEqual(
|
|
1070
|
+
MeetingUtil.requireHostEndMeetingBeforeLeave([
|
|
1071
|
+
'REQUIRE_HOST_END_MEETING_BEFORE_LEAVE',
|
|
1072
|
+
'END_MEETING',
|
|
1073
|
+
]),
|
|
1074
|
+
true
|
|
1075
|
+
);
|
|
1076
|
+
assert.deepEqual(
|
|
1077
|
+
MeetingUtil.requireHostEndMeetingBeforeLeave([
|
|
1078
|
+
'REQUIRE_HOST_END_MEETING_BEFORE_LEAVE',
|
|
1079
|
+
'LEAVE_MEETING',
|
|
1080
|
+
]),
|
|
1081
|
+
true
|
|
1082
|
+
);
|
|
1083
|
+
assert.deepEqual(
|
|
1084
|
+
MeetingUtil.requireHostEndMeetingBeforeLeave([
|
|
1085
|
+
'REQUIRE_HOST_END_MEETING_BEFORE_LEAVE',
|
|
1086
|
+
'LEAVE_MEETING',
|
|
1087
|
+
'END_MEETING',
|
|
1088
|
+
]),
|
|
1089
|
+
true
|
|
1090
|
+
);
|
|
1091
|
+
assert.deepEqual(MeetingUtil.requireHostEndMeetingBeforeLeave(['END_MEETING']), true);
|
|
1092
|
+
assert.deepEqual(MeetingUtil.requireHostEndMeetingBeforeLeave([]), false);
|
|
1093
|
+
});
|
|
1094
|
+
});
|
|
1095
|
+
|
|
942
1096
|
describe('canUserLock', () => {
|
|
943
1097
|
it('works as expected', () => {
|
|
944
1098
|
assert.deepEqual(
|
|
@@ -972,15 +1126,18 @@ describe('plugin-meetings', () => {
|
|
|
972
1126
|
{functionName: 'canStartManualCaption', displayHint: 'MANUAL_CAPTION_START'},
|
|
973
1127
|
{functionName: 'canStopManualCaption', displayHint: 'MANUAL_CAPTION_STOP'},
|
|
974
1128
|
|
|
975
|
-
{functionName: 'isLocalRecordingStarted',displayHint:'LOCAL_RECORDING_STATUS_STARTED'},
|
|
1129
|
+
{functionName: 'isLocalRecordingStarted', displayHint: 'LOCAL_RECORDING_STATUS_STARTED'},
|
|
976
1130
|
{functionName: 'isLocalRecordingStopped', displayHint: 'LOCAL_RECORDING_STATUS_STOPPED'},
|
|
977
1131
|
{functionName: 'isLocalRecordingPaused', displayHint: 'LOCAL_RECORDING_STATUS_PAUSED'},
|
|
978
|
-
{functionName: 'isLocalStreamingStarted',displayHint:'STREAMING_STATUS_STARTED'},
|
|
1132
|
+
{functionName: 'isLocalStreamingStarted', displayHint: 'STREAMING_STATUS_STARTED'},
|
|
979
1133
|
{functionName: 'isLocalStreamingStopped', displayHint: 'STREAMING_STATUS_STOPPED'},
|
|
980
1134
|
|
|
981
1135
|
{functionName: 'isManualCaptionActive', displayHint: 'MANUAL_CAPTION_STATUS_ACTIVE'},
|
|
982
1136
|
|
|
983
|
-
{
|
|
1137
|
+
{
|
|
1138
|
+
functionName: 'isSpokenLanguageAutoDetectionEnabled',
|
|
1139
|
+
displayHint: 'SPOKEN_LANGUAGE_AUTO_DETECTION_ENABLED',
|
|
1140
|
+
},
|
|
984
1141
|
|
|
985
1142
|
{functionName: 'isWebexAssistantActive', displayHint: 'WEBEX_ASSISTANT_STATUS_ACTIVE'},
|
|
986
1143
|
{functionName: 'canViewCaptionPanel', displayHint: 'ENABLE_CAPTION_PANEL'},
|
|
@@ -1446,11 +1603,9 @@ describe('plugin-meetings', () => {
|
|
|
1446
1603
|
id: 'selfId123',
|
|
1447
1604
|
},
|
|
1448
1605
|
},
|
|
1606
|
+
metaData: {id: 'some hash tree metadata'},
|
|
1449
1607
|
dataSets: [{name: 'dataset1', url: 'http://dataset.com'}],
|
|
1450
|
-
mediaConnections: [
|
|
1451
|
-
{mediaId: 'mediaId456'},
|
|
1452
|
-
{someOtherField: 'value'},
|
|
1453
|
-
],
|
|
1608
|
+
mediaConnections: [{mediaId: 'mediaId456'}, {someOtherField: 'value'}],
|
|
1454
1609
|
},
|
|
1455
1610
|
};
|
|
1456
1611
|
});
|
|
@@ -1466,6 +1621,7 @@ describe('plugin-meetings', () => {
|
|
|
1466
1621
|
locusId: '12345',
|
|
1467
1622
|
selfId: 'selfId123',
|
|
1468
1623
|
mediaId: 'mediaId456',
|
|
1624
|
+
metadata: {id: 'some hash tree metadata'},
|
|
1469
1625
|
});
|
|
1470
1626
|
});
|
|
1471
1627
|
|
|
@@ -1495,20 +1651,275 @@ describe('plugin-meetings', () => {
|
|
|
1495
1651
|
locusUrl: 'https://locus-a.wbx2.com/locus/api/v1/loci/12345',
|
|
1496
1652
|
locusId: '12345',
|
|
1497
1653
|
selfId: 'selfId123',
|
|
1654
|
+
metadata: {id: 'some hash tree metadata'},
|
|
1498
1655
|
});
|
|
1499
1656
|
assert.isUndefined(result.mediaId);
|
|
1500
1657
|
});
|
|
1501
1658
|
|
|
1502
1659
|
it('handles mediaConnections without mediaId', () => {
|
|
1503
|
-
response.body.mediaConnections = [
|
|
1504
|
-
{someField: 'value1'},
|
|
1505
|
-
{anotherField: 'value2'},
|
|
1506
|
-
];
|
|
1660
|
+
response.body.mediaConnections = [{someField: 'value1'}, {anotherField: 'value2'}];
|
|
1507
1661
|
|
|
1508
1662
|
const result = MeetingUtil.parseLocusJoin(response);
|
|
1509
1663
|
|
|
1510
1664
|
assert.isUndefined(result.mediaId);
|
|
1511
1665
|
});
|
|
1512
1666
|
});
|
|
1667
|
+
|
|
1668
|
+
describe('#sanitizeWebSocketUrl', () => {
|
|
1669
|
+
it('extracts protocol, host, and pathname from URL', () => {
|
|
1670
|
+
const url = 'wss://example.com:443/mercury/path?token=secret&key=value#fragment';
|
|
1671
|
+
const result = MeetingUtil.sanitizeWebSocketUrl(url);
|
|
1672
|
+
|
|
1673
|
+
assert.equal(result, 'wss://example.com:443/mercury/path');
|
|
1674
|
+
});
|
|
1675
|
+
|
|
1676
|
+
it('handles URL without query string or hash', () => {
|
|
1677
|
+
const url = 'wss://example.com/path';
|
|
1678
|
+
const result = MeetingUtil.sanitizeWebSocketUrl(url);
|
|
1679
|
+
|
|
1680
|
+
assert.equal(result, 'wss://example.com/path');
|
|
1681
|
+
});
|
|
1682
|
+
|
|
1683
|
+
it('removes authentication from URL', () => {
|
|
1684
|
+
const url = 'wss://user:password@example.com/path?token=secret';
|
|
1685
|
+
const result = MeetingUtil.sanitizeWebSocketUrl(url);
|
|
1686
|
+
|
|
1687
|
+
assert.equal(result, 'wss://example.com/path');
|
|
1688
|
+
});
|
|
1689
|
+
|
|
1690
|
+
it('returns empty string for null or undefined', () => {
|
|
1691
|
+
assert.equal(MeetingUtil.sanitizeWebSocketUrl(null), '');
|
|
1692
|
+
assert.equal(MeetingUtil.sanitizeWebSocketUrl(undefined), '');
|
|
1693
|
+
});
|
|
1694
|
+
|
|
1695
|
+
it('returns empty string for non-string input', () => {
|
|
1696
|
+
assert.equal(MeetingUtil.sanitizeWebSocketUrl(123), '');
|
|
1697
|
+
assert.equal(MeetingUtil.sanitizeWebSocketUrl({}), '');
|
|
1698
|
+
});
|
|
1699
|
+
|
|
1700
|
+
it('returns empty string for invalid URL', () => {
|
|
1701
|
+
const result = MeetingUtil.sanitizeWebSocketUrl('not a valid url');
|
|
1702
|
+
|
|
1703
|
+
assert.equal(result, '');
|
|
1704
|
+
});
|
|
1705
|
+
|
|
1706
|
+
it('handles URL without pathname', () => {
|
|
1707
|
+
const url = 'wss://example.com?query=value';
|
|
1708
|
+
const result = MeetingUtil.sanitizeWebSocketUrl(url);
|
|
1709
|
+
|
|
1710
|
+
assert.equal(result, 'wss://example.com');
|
|
1711
|
+
});
|
|
1712
|
+
});
|
|
1713
|
+
|
|
1714
|
+
describe('#_urlsPartiallyMatch', () => {
|
|
1715
|
+
it('returns true when URLs match exactly (ignoring query and hash)', () => {
|
|
1716
|
+
const url1 = 'wss://example.com:443/path?token=abc#fragment1';
|
|
1717
|
+
const url2 = 'wss://example.com:443/path?token=xyz#fragment2';
|
|
1718
|
+
|
|
1719
|
+
assert.isTrue(MeetingUtil._urlsPartiallyMatch(url1, url2));
|
|
1720
|
+
});
|
|
1721
|
+
|
|
1722
|
+
it('returns true when one URL is proxied and ends with the other', () => {
|
|
1723
|
+
const url1 = 'wss://other.example.com/somepath/mercury.example.com/v1/path';
|
|
1724
|
+
const url2 = 'wss://mercury.example.com/v1/path';
|
|
1725
|
+
|
|
1726
|
+
assert.isTrue(MeetingUtil._urlsPartiallyMatch(url1, url2));
|
|
1727
|
+
});
|
|
1728
|
+
|
|
1729
|
+
it('returns true when the second URL is proxied', () => {
|
|
1730
|
+
const url1 = 'wss://mercury.example.com/v1/path';
|
|
1731
|
+
const url2 = 'wss://other.example.com/somepath/mercury.example.com/v1/path';
|
|
1732
|
+
|
|
1733
|
+
assert.isTrue(MeetingUtil._urlsPartiallyMatch(url1, url2));
|
|
1734
|
+
});
|
|
1735
|
+
|
|
1736
|
+
it('returns false when hosts differ and no partial match', () => {
|
|
1737
|
+
const url1 = 'wss://example1.com/path';
|
|
1738
|
+
const url2 = 'wss://example2.com/path';
|
|
1739
|
+
|
|
1740
|
+
assert.isFalse(MeetingUtil._urlsPartiallyMatch(url1, url2));
|
|
1741
|
+
});
|
|
1742
|
+
|
|
1743
|
+
it('returns false when pathnames differ and no partial match', () => {
|
|
1744
|
+
const url1 = 'wss://example.com/path1';
|
|
1745
|
+
const url2 = 'wss://example.com/path2';
|
|
1746
|
+
|
|
1747
|
+
assert.isFalse(MeetingUtil._urlsPartiallyMatch(url1, url2));
|
|
1748
|
+
});
|
|
1749
|
+
|
|
1750
|
+
it('returns false when either URL is null or undefined', () => {
|
|
1751
|
+
const url = 'wss://example.com/path';
|
|
1752
|
+
|
|
1753
|
+
assert.isFalse(MeetingUtil._urlsPartiallyMatch(null, url));
|
|
1754
|
+
assert.isFalse(MeetingUtil._urlsPartiallyMatch(url, null));
|
|
1755
|
+
assert.isFalse(MeetingUtil._urlsPartiallyMatch(undefined, url));
|
|
1756
|
+
assert.isFalse(MeetingUtil._urlsPartiallyMatch(url, undefined));
|
|
1757
|
+
});
|
|
1758
|
+
|
|
1759
|
+
it('returns false when both URLs are null', () => {
|
|
1760
|
+
assert.isFalse(MeetingUtil._urlsPartiallyMatch(null, null));
|
|
1761
|
+
});
|
|
1762
|
+
|
|
1763
|
+
it('returns false when URL parsing fails', () => {
|
|
1764
|
+
const url1 = 'invalid url';
|
|
1765
|
+
const url2 = 'wss://example.com/path';
|
|
1766
|
+
|
|
1767
|
+
assert.isFalse(MeetingUtil._urlsPartiallyMatch(url1, url2));
|
|
1768
|
+
});
|
|
1769
|
+
});
|
|
1770
|
+
|
|
1771
|
+
describe('#getSocketUrlInfo', () => {
|
|
1772
|
+
it('returns socket URL info when URLs differ', () => {
|
|
1773
|
+
const testWebex = {
|
|
1774
|
+
internal: {
|
|
1775
|
+
mercury: {
|
|
1776
|
+
socket: {
|
|
1777
|
+
url: 'wss://mercury.example.com:443/path?token=abc',
|
|
1778
|
+
},
|
|
1779
|
+
},
|
|
1780
|
+
device: {
|
|
1781
|
+
webSocketUrl: 'wss://device.example.com:443/path?token=xyz',
|
|
1782
|
+
},
|
|
1783
|
+
},
|
|
1784
|
+
};
|
|
1785
|
+
|
|
1786
|
+
const result = MeetingUtil.getSocketUrlInfo(testWebex);
|
|
1787
|
+
|
|
1788
|
+
assert.isTrue(result.hasMismatchedSocket);
|
|
1789
|
+
assert.equal(result.mercurySocketUrl, 'wss://mercury.example.com:443/path');
|
|
1790
|
+
assert.equal(result.deviceSocketUrl, 'wss://device.example.com:443/path');
|
|
1791
|
+
});
|
|
1792
|
+
|
|
1793
|
+
it('returns socket URL info when URLs match', () => {
|
|
1794
|
+
const testWebex = {
|
|
1795
|
+
internal: {
|
|
1796
|
+
mercury: {
|
|
1797
|
+
socket: {
|
|
1798
|
+
url: 'wss://example.com:443/path?token=abc',
|
|
1799
|
+
},
|
|
1800
|
+
},
|
|
1801
|
+
device: {
|
|
1802
|
+
webSocketUrl: 'wss://example.com:443/path?token=xyz',
|
|
1803
|
+
},
|
|
1804
|
+
},
|
|
1805
|
+
};
|
|
1806
|
+
|
|
1807
|
+
const result = MeetingUtil.getSocketUrlInfo(testWebex);
|
|
1808
|
+
|
|
1809
|
+
assert.isFalse(result.hasMismatchedSocket);
|
|
1810
|
+
assert.equal(result.mercurySocketUrl, 'wss://example.com:443/path');
|
|
1811
|
+
assert.equal(result.deviceSocketUrl, 'wss://example.com:443/path');
|
|
1812
|
+
});
|
|
1813
|
+
|
|
1814
|
+
it('returns hasMismatchedSocket as false when one URL is proxied (partial match)', () => {
|
|
1815
|
+
const testWebex = {
|
|
1816
|
+
internal: {
|
|
1817
|
+
mercury: {
|
|
1818
|
+
socket: {
|
|
1819
|
+
url: 'wss://other.example.com/somepath/mercury.example.com/v1/apps/wx2/registrations/00000000-0000-0000-0000-000000000000/messages',
|
|
1820
|
+
},
|
|
1821
|
+
},
|
|
1822
|
+
device: {
|
|
1823
|
+
webSocketUrl:
|
|
1824
|
+
'wss://mercury.example.com/v1/apps/wx2/registrations/00000000-0000-0000-0000-000000000000/messages',
|
|
1825
|
+
},
|
|
1826
|
+
},
|
|
1827
|
+
};
|
|
1828
|
+
|
|
1829
|
+
const result = MeetingUtil.getSocketUrlInfo(testWebex);
|
|
1830
|
+
|
|
1831
|
+
assert.isFalse(result.hasMismatchedSocket);
|
|
1832
|
+
assert.equal(
|
|
1833
|
+
result.mercurySocketUrl,
|
|
1834
|
+
'wss://other.example.com/somepath/mercury.example.com/v1/apps/wx2/registrations/00000000-0000-0000-0000-000000000000/messages'
|
|
1835
|
+
);
|
|
1836
|
+
assert.equal(
|
|
1837
|
+
result.deviceSocketUrl,
|
|
1838
|
+
'wss://mercury.example.com/v1/apps/wx2/registrations/00000000-0000-0000-0000-000000000000/messages'
|
|
1839
|
+
);
|
|
1840
|
+
});
|
|
1841
|
+
|
|
1842
|
+
it('returns false for hasMismatchedSocket when mercury socket URL is missing', () => {
|
|
1843
|
+
const testWebex = {
|
|
1844
|
+
internal: {
|
|
1845
|
+
mercury: {
|
|
1846
|
+
socket: {},
|
|
1847
|
+
},
|
|
1848
|
+
device: {
|
|
1849
|
+
webSocketUrl: 'wss://device.example.com:443/path',
|
|
1850
|
+
},
|
|
1851
|
+
},
|
|
1852
|
+
};
|
|
1853
|
+
|
|
1854
|
+
const result = MeetingUtil.getSocketUrlInfo(testWebex);
|
|
1855
|
+
|
|
1856
|
+
assert.isFalse(result.hasMismatchedSocket);
|
|
1857
|
+
assert.equal(result.mercurySocketUrl, '');
|
|
1858
|
+
assert.equal(result.deviceSocketUrl, 'wss://device.example.com:443/path');
|
|
1859
|
+
});
|
|
1860
|
+
|
|
1861
|
+
it('returns false for hasMismatchedSocket when device socket URL is missing', () => {
|
|
1862
|
+
const testWebex = {
|
|
1863
|
+
internal: {
|
|
1864
|
+
mercury: {
|
|
1865
|
+
socket: {
|
|
1866
|
+
url: 'wss://mercury.example.com:443/path',
|
|
1867
|
+
},
|
|
1868
|
+
},
|
|
1869
|
+
device: {},
|
|
1870
|
+
},
|
|
1871
|
+
};
|
|
1872
|
+
|
|
1873
|
+
const result = MeetingUtil.getSocketUrlInfo(testWebex);
|
|
1874
|
+
|
|
1875
|
+
assert.isFalse(result.hasMismatchedSocket);
|
|
1876
|
+
assert.equal(result.mercurySocketUrl, 'wss://mercury.example.com:443/path');
|
|
1877
|
+
assert.equal(result.deviceSocketUrl, '');
|
|
1878
|
+
});
|
|
1879
|
+
|
|
1880
|
+
it('returns default values when webex object is missing properties', () => {
|
|
1881
|
+
const testWebex = {
|
|
1882
|
+
internal: {},
|
|
1883
|
+
};
|
|
1884
|
+
|
|
1885
|
+
const result = MeetingUtil.getSocketUrlInfo(testWebex);
|
|
1886
|
+
|
|
1887
|
+
assert.isFalse(result.hasMismatchedSocket);
|
|
1888
|
+
assert.equal(result.mercurySocketUrl, '');
|
|
1889
|
+
assert.equal(result.deviceSocketUrl, '');
|
|
1890
|
+
});
|
|
1891
|
+
|
|
1892
|
+
it('handles error gracefully and returns default values', () => {
|
|
1893
|
+
const testWebex = null;
|
|
1894
|
+
|
|
1895
|
+
const result = MeetingUtil.getSocketUrlInfo(testWebex);
|
|
1896
|
+
|
|
1897
|
+
assert.isFalse(result.hasMismatchedSocket);
|
|
1898
|
+
assert.equal(result.mercurySocketUrl, '');
|
|
1899
|
+
assert.equal(result.deviceSocketUrl, '');
|
|
1900
|
+
});
|
|
1901
|
+
|
|
1902
|
+
it('sanitizes URLs by removing query parameters', () => {
|
|
1903
|
+
const testWebex = {
|
|
1904
|
+
internal: {
|
|
1905
|
+
mercury: {
|
|
1906
|
+
socket: {
|
|
1907
|
+
url: 'wss://mercury.example.com/path?secret=token123&key=value',
|
|
1908
|
+
},
|
|
1909
|
+
},
|
|
1910
|
+
device: {
|
|
1911
|
+
webSocketUrl: 'wss://device.example.com/path?secret=differenttoken&key=value',
|
|
1912
|
+
},
|
|
1913
|
+
},
|
|
1914
|
+
};
|
|
1915
|
+
|
|
1916
|
+
const result = MeetingUtil.getSocketUrlInfo(testWebex);
|
|
1917
|
+
|
|
1918
|
+
assert.notInclude(result.mercurySocketUrl, 'secret');
|
|
1919
|
+
assert.notInclude(result.mercurySocketUrl, 'token123');
|
|
1920
|
+
assert.notInclude(result.deviceSocketUrl, 'secret');
|
|
1921
|
+
assert.notInclude(result.deviceSocketUrl, 'differenttoken');
|
|
1922
|
+
});
|
|
1923
|
+
});
|
|
1513
1924
|
});
|
|
1514
1925
|
});
|