@webex/plugin-meetings 3.0.0-beta.31 → 3.0.0-beta.310
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/README.md +46 -8
- package/dist/annotation/annotation.types.js +7 -0
- package/dist/annotation/annotation.types.js.map +1 -0
- package/dist/annotation/constants.js +49 -0
- package/dist/annotation/constants.js.map +1 -0
- package/dist/annotation/index.js +342 -0
- package/dist/annotation/index.js.map +1 -0
- package/dist/breakouts/breakout.js +94 -15
- package/dist/breakouts/breakout.js.map +1 -1
- package/dist/breakouts/edit-lock-error.js +52 -0
- package/dist/breakouts/edit-lock-error.js.map +1 -0
- package/dist/breakouts/events.js +45 -0
- package/dist/breakouts/events.js.map +1 -0
- package/dist/breakouts/index.js +709 -35
- package/dist/breakouts/index.js.map +1 -1
- package/dist/breakouts/utils.js +45 -1
- package/dist/breakouts/utils.js.map +1 -1
- package/dist/common/errors/no-meeting-info.js +51 -0
- package/dist/common/errors/no-meeting-info.js.map +1 -0
- package/dist/common/errors/reclaim-host-role-errors.js +158 -0
- package/dist/common/errors/reclaim-host-role-errors.js.map +1 -0
- package/dist/common/errors/webex-errors.js +48 -7
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/common/logs/logger-proxy.js +1 -1
- package/dist/common/logs/logger-proxy.js.map +1 -1
- package/dist/common/logs/request.js +5 -1
- package/dist/common/logs/request.js.map +1 -1
- package/dist/common/queue.js +24 -9
- package/dist/common/queue.js.map +1 -1
- package/dist/config.js +5 -11
- package/dist/config.js.map +1 -1
- package/dist/constants.js +233 -29
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/enums.js +14 -2
- package/dist/controls-options-manager/enums.js.map +1 -1
- package/dist/controls-options-manager/index.js +109 -15
- package/dist/controls-options-manager/index.js.map +1 -1
- package/dist/controls-options-manager/types.js +7 -0
- package/dist/controls-options-manager/types.js.map +1 -0
- package/dist/controls-options-manager/util.js +309 -18
- package/dist/controls-options-manager/util.js.map +1 -1
- package/dist/index.js +112 -1
- package/dist/index.js.map +1 -1
- package/dist/interpretation/collection.js +23 -0
- package/dist/interpretation/collection.js.map +1 -0
- package/dist/interpretation/index.js +366 -0
- package/dist/interpretation/index.js.map +1 -0
- package/dist/interpretation/siLanguage.js +25 -0
- package/dist/interpretation/siLanguage.js.map +1 -0
- package/dist/locus-info/controlsUtils.js +91 -2
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +383 -62
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/infoUtils.js +7 -1
- package/dist/locus-info/infoUtils.js.map +1 -1
- package/dist/locus-info/mediaSharesUtils.js +57 -1
- package/dist/locus-info/mediaSharesUtils.js.map +1 -1
- package/dist/locus-info/parser.js +249 -72
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/locus-info/selfUtils.js +89 -14
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/index.js +61 -116
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +73 -124
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +82 -2
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +3777 -2929
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +292 -0
- package/dist/meeting/locusMediaRequest.js.map +1 -0
- package/dist/meeting/muteState.js +230 -124
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +260 -196
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +601 -417
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/index.js +73 -7
- package/dist/meeting-info/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +192 -51
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/util.js +1 -1
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js +36 -36
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/collection.js +39 -0
- package/dist/meetings/collection.js.map +1 -1
- package/dist/meetings/index.js +415 -115
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/meetings.types.js +7 -0
- package/dist/meetings/meetings.types.js.map +1 -0
- package/dist/meetings/request.js +2 -0
- package/dist/meetings/request.js.map +1 -1
- package/dist/meetings/util.js +72 -6
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +58 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js +25 -0
- package/dist/member/types.js.map +1 -0
- package/dist/member/util.js +132 -25
- package/dist/member/util.js.map +1 -1
- package/dist/members/collection.js +10 -0
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +102 -6
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +106 -38
- package/dist/members/request.js.map +1 -1
- package/dist/members/types.js +15 -0
- package/dist/members/types.js.map +1 -0
- package/dist/members/util.js +326 -232
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +13 -5
- package/dist/metrics/constants.js.map +1 -1
- package/dist/metrics/index.js +1 -468
- package/dist/metrics/index.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +238 -49
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/receiveSlot.js +29 -16
- package/dist/multistream/receiveSlot.js.map +1 -1
- package/dist/multistream/receiveSlotManager.js +39 -36
- package/dist/multistream/receiveSlotManager.js.map +1 -1
- package/dist/multistream/remoteMedia.js +44 -18
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.js +60 -3
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +209 -59
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +233 -0
- package/dist/multistream/sendSlotManager.js.map +1 -0
- package/dist/reachability/index.js +225 -59
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/request.js +17 -8
- package/dist/reachability/request.js.map +1 -1
- package/dist/reconnection-manager/index.js +201 -156
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/recording-controller/index.js +21 -2
- package/dist/recording-controller/index.js.map +1 -1
- package/dist/recording-controller/util.js +9 -8
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/roap/index.js +62 -32
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +112 -97
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +95 -36
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/rtcMetrics/constants.js +12 -0
- package/dist/rtcMetrics/constants.js.map +1 -0
- package/dist/rtcMetrics/index.js +117 -0
- package/dist/rtcMetrics/index.js.map +1 -0
- package/dist/statsAnalyzer/index.js +86 -78
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +11 -10
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/dist/types/annotation/annotation.types.d.ts +42 -0
- package/dist/types/annotation/constants.d.ts +31 -0
- package/dist/types/annotation/index.d.ts +117 -0
- package/dist/types/breakouts/edit-lock-error.d.ts +15 -0
- package/dist/types/breakouts/events.d.ts +8 -0
- package/dist/types/breakouts/utils.d.ts +14 -0
- package/dist/types/common/errors/no-meeting-info.d.ts +14 -0
- package/dist/types/common/errors/reclaim-host-role-errors.d.ts +60 -0
- package/dist/types/common/errors/webex-errors.d.ts +25 -1
- package/dist/types/common/logs/request.d.ts +2 -0
- package/dist/types/common/queue.d.ts +9 -7
- package/dist/types/config.d.ts +1 -7
- package/dist/types/constants.d.ts +194 -24
- package/dist/types/controls-options-manager/enums.d.ts +11 -1
- package/dist/types/controls-options-manager/index.d.ts +17 -1
- package/dist/types/controls-options-manager/types.d.ts +43 -0
- package/dist/types/controls-options-manager/util.d.ts +1 -7
- package/dist/types/index.d.ts +6 -4
- package/dist/types/interpretation/collection.d.ts +5 -0
- package/dist/types/interpretation/index.d.ts +5 -0
- package/dist/types/interpretation/siLanguage.d.ts +5 -0
- package/dist/types/locus-info/index.d.ts +57 -4
- package/dist/types/locus-info/parser.d.ts +67 -6
- package/dist/types/media/index.d.ts +2 -0
- package/dist/types/media/properties.d.ts +34 -48
- package/dist/types/meeting/in-meeting-actions.d.ts +82 -2
- package/dist/types/meeting/index.d.ts +463 -510
- package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
- package/dist/types/meeting/muteState.d.ts +99 -23
- package/dist/types/meeting/request.d.ts +72 -43
- package/dist/types/meeting/util.d.ts +101 -1
- package/dist/types/meeting-info/index.d.ts +13 -1
- package/dist/types/meeting-info/meeting-info-v2.d.ts +31 -1
- package/dist/types/meetings/collection.d.ts +17 -0
- package/dist/types/meetings/index.d.ts +98 -20
- package/dist/types/meetings/meetings.types.d.ts +4 -0
- package/dist/types/member/index.d.ts +14 -0
- package/dist/types/member/types.d.ts +32 -0
- package/dist/types/members/collection.d.ts +5 -0
- package/dist/types/members/index.d.ts +35 -2
- package/dist/types/members/request.d.ts +73 -9
- package/dist/types/members/types.d.ts +25 -0
- package/dist/types/members/util.d.ts +214 -1
- package/dist/types/metrics/constants.d.ts +12 -4
- package/dist/types/metrics/index.d.ts +4 -119
- package/dist/types/multistream/mediaRequestManager.d.ts +73 -5
- package/dist/types/multistream/receiveSlot.d.ts +13 -11
- package/dist/types/multistream/receiveSlotManager.d.ts +14 -4
- package/dist/types/multistream/remoteMedia.d.ts +8 -29
- package/dist/types/multistream/remoteMediaGroup.d.ts +0 -9
- package/dist/types/multistream/remoteMediaManager.d.ts +46 -2
- package/dist/types/multistream/sendSlotManager.d.ts +61 -0
- package/dist/types/reachability/index.d.ts +61 -7
- package/dist/types/reachability/request.d.ts +7 -3
- package/dist/types/reconnection-manager/index.d.ts +9 -0
- package/dist/types/recording-controller/index.d.ts +15 -1
- package/dist/types/recording-controller/util.d.ts +5 -4
- package/dist/types/roap/index.d.ts +2 -1
- package/dist/types/roap/request.d.ts +15 -11
- package/dist/types/roap/turnDiscovery.d.ts +21 -3
- package/dist/types/rtcMetrics/constants.d.ts +4 -0
- package/dist/types/rtcMetrics/index.d.ts +47 -0
- package/dist/types/statsAnalyzer/index.d.ts +7 -1
- package/dist/types/webinar/collection.d.ts +16 -0
- package/dist/types/webinar/index.d.ts +5 -0
- package/dist/webinar/collection.js +44 -0
- package/dist/webinar/collection.js.map +1 -0
- package/dist/webinar/index.js +69 -0
- package/dist/webinar/index.js.map +1 -0
- package/package.json +23 -20
- package/src/annotation/annotation.types.ts +50 -0
- package/src/annotation/constants.ts +36 -0
- package/src/annotation/index.ts +328 -0
- package/src/breakouts/README.md +42 -12
- package/src/breakouts/breakout.ts +67 -9
- package/src/breakouts/edit-lock-error.ts +25 -0
- package/src/breakouts/events.ts +56 -0
- package/src/breakouts/index.ts +592 -20
- package/src/breakouts/utils.ts +42 -0
- package/src/common/errors/no-meeting-info.ts +24 -0
- package/src/common/errors/reclaim-host-role-errors.ts +134 -0
- package/src/common/errors/webex-errors.ts +44 -2
- package/src/common/logs/logger-proxy.ts +1 -1
- package/src/common/logs/request.ts +5 -1
- package/src/common/queue.ts +22 -8
- package/src/config.ts +4 -10
- package/src/constants.ts +221 -19
- package/src/controls-options-manager/enums.ts +12 -0
- package/src/controls-options-manager/index.ts +116 -21
- package/src/controls-options-manager/types.ts +59 -0
- package/src/controls-options-manager/util.ts +294 -14
- package/src/index.ts +40 -0
- package/src/interpretation/README.md +60 -0
- package/src/interpretation/collection.ts +19 -0
- package/src/interpretation/index.ts +332 -0
- package/src/interpretation/siLanguage.ts +18 -0
- package/src/locus-info/controlsUtils.ts +108 -0
- package/src/locus-info/index.ts +413 -59
- package/src/locus-info/infoUtils.ts +10 -2
- package/src/locus-info/mediaSharesUtils.ts +64 -0
- package/src/locus-info/parser.ts +258 -47
- package/src/locus-info/selfUtils.ts +81 -5
- package/src/media/index.ts +102 -122
- package/src/media/properties.ts +87 -110
- package/src/meeting/in-meeting-actions.ts +163 -3
- package/src/meeting/index.ts +3132 -2541
- package/src/meeting/locusMediaRequest.ts +313 -0
- package/src/meeting/muteState.ts +229 -131
- package/src/meeting/request.ts +177 -121
- package/src/meeting/util.ts +588 -394
- package/src/meeting-info/index.ts +81 -8
- package/src/meeting-info/meeting-info-v2.ts +170 -14
- package/src/meeting-info/util.ts +1 -1
- package/src/meeting-info/utilv2.ts +23 -23
- package/src/meetings/collection.ts +33 -0
- package/src/meetings/index.ts +445 -123
- package/src/meetings/meetings.types.ts +12 -0
- package/src/meetings/request.ts +2 -0
- package/src/meetings/util.ts +80 -11
- package/src/member/index.ts +58 -0
- package/src/member/types.ts +38 -0
- package/src/member/util.ts +141 -25
- package/src/members/collection.ts +8 -0
- package/src/members/index.ts +134 -8
- package/src/members/request.ts +97 -17
- package/src/members/types.ts +29 -0
- package/src/members/util.ts +333 -240
- package/src/metrics/constants.ts +12 -4
- package/src/metrics/index.ts +1 -490
- package/src/multistream/mediaRequestManager.ts +289 -79
- package/src/multistream/receiveSlot.ts +31 -17
- package/src/multistream/receiveSlotManager.ts +34 -24
- package/src/multistream/remoteMedia.ts +27 -2
- package/src/multistream/remoteMediaGroup.ts +59 -0
- package/src/multistream/remoteMediaManager.ts +148 -30
- package/src/multistream/sendSlotManager.ts +170 -0
- package/src/reachability/index.ts +228 -37
- package/src/reachability/request.ts +17 -8
- package/src/reconnection-manager/index.ts +83 -56
- package/src/recording-controller/index.ts +20 -3
- package/src/recording-controller/util.ts +26 -9
- package/src/roap/index.ts +63 -32
- package/src/roap/request.ts +100 -104
- package/src/roap/turnDiscovery.ts +48 -26
- package/src/rtcMetrics/constants.ts +3 -0
- package/src/rtcMetrics/index.ts +100 -0
- package/src/statsAnalyzer/index.ts +105 -91
- package/src/statsAnalyzer/mqaUtil.ts +13 -14
- package/src/webinar/collection.ts +31 -0
- package/src/webinar/index.ts +62 -0
- package/test/integration/spec/converged-space-meetings.js +60 -3
- package/test/integration/spec/journey.js +320 -261
- package/test/integration/spec/space-meeting.js +76 -3
- package/test/unit/spec/annotation/index.ts +418 -0
- package/test/unit/spec/breakouts/breakout.ts +118 -28
- package/test/unit/spec/breakouts/edit-lock-error.ts +30 -0
- package/test/unit/spec/breakouts/events.ts +89 -0
- package/test/unit/spec/breakouts/index.ts +1395 -69
- package/test/unit/spec/breakouts/utils.js +52 -1
- package/test/unit/spec/common/queue.js +31 -2
- package/test/unit/spec/controls-options-manager/index.js +163 -0
- package/test/unit/spec/controls-options-manager/util.js +576 -60
- package/test/unit/spec/fixture/locus.js +1 -0
- package/test/unit/spec/interpretation/collection.ts +15 -0
- package/test/unit/spec/interpretation/index.ts +589 -0
- package/test/unit/spec/interpretation/siLanguage.ts +28 -0
- package/test/unit/spec/locus-info/controlsUtils.js +316 -43
- package/test/unit/spec/locus-info/index.js +1304 -33
- package/test/unit/spec/locus-info/infoUtils.js +37 -15
- package/test/unit/spec/locus-info/lib/SeqCmp.json +16 -0
- package/test/unit/spec/locus-info/mediaSharesUtils.ts +32 -0
- package/test/unit/spec/locus-info/parser.js +116 -35
- package/test/unit/spec/locus-info/selfConstant.js +27 -4
- package/test/unit/spec/locus-info/selfUtils.js +208 -17
- package/test/unit/spec/media/index.ts +104 -37
- package/test/unit/spec/media/properties.ts +2 -2
- package/test/unit/spec/meeting/in-meeting-actions.ts +81 -3
- package/test/unit/spec/meeting/index.js +5216 -1956
- package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
- package/test/unit/spec/meeting/muteState.js +408 -208
- package/test/unit/spec/meeting/request.js +483 -49
- package/test/unit/spec/meeting/utils.js +679 -64
- package/test/unit/spec/meeting-info/index.js +300 -0
- package/test/unit/spec/meeting-info/meetinginfov2.js +526 -5
- package/test/unit/spec/meeting-info/utilv2.js +21 -0
- package/test/unit/spec/meetings/collection.js +26 -0
- package/test/unit/spec/meetings/index.js +1011 -205
- package/test/unit/spec/meetings/utils.js +202 -2
- package/test/unit/spec/member/index.js +61 -6
- package/test/unit/spec/member/util.js +510 -34
- package/test/unit/spec/members/index.js +432 -1
- package/test/unit/spec/members/request.js +206 -27
- package/test/unit/spec/members/utils.js +210 -0
- package/test/unit/spec/metrics/index.js +1 -50
- package/test/unit/spec/multistream/mediaRequestManager.ts +803 -162
- package/test/unit/spec/multistream/receiveSlot.ts +28 -20
- package/test/unit/spec/multistream/receiveSlotManager.ts +32 -30
- package/test/unit/spec/multistream/remoteMedia.ts +30 -0
- package/test/unit/spec/multistream/remoteMediaGroup.ts +266 -0
- package/test/unit/spec/multistream/remoteMediaManager.ts +326 -0
- package/test/unit/spec/multistream/sendSlotManager.ts +242 -0
- package/test/unit/spec/reachability/index.ts +549 -9
- package/test/unit/spec/reachability/request.js +68 -0
- package/test/unit/spec/reconnection-manager/index.js +85 -9
- package/test/unit/spec/recording-controller/index.js +294 -218
- package/test/unit/spec/recording-controller/util.js +223 -96
- package/test/unit/spec/roap/index.ts +178 -64
- package/test/unit/spec/roap/request.ts +203 -85
- package/test/unit/spec/roap/turnDiscovery.ts +82 -36
- package/test/unit/spec/rtcMetrics/index.ts +73 -0
- package/test/unit/spec/stats-analyzer/index.js +136 -2
- package/test/unit/spec/webinar/collection.ts +13 -0
- package/test/unit/spec/webinar/index.ts +60 -0
- package/test/utils/integrationTestUtils.js +46 -0
- package/test/utils/testUtils.js +0 -52
- package/dist/meeting/effectsState.js +0 -262
- package/dist/meeting/effectsState.js.map +0 -1
- package/dist/metrics/config.js +0 -299
- package/dist/metrics/config.js.map +0 -1
- package/dist/types/meeting/effectsState.d.ts +0 -42
- package/dist/types/metrics/config.d.ts +0 -178
- package/src/index.js +0 -16
- package/src/meeting/effectsState.ts +0 -211
- package/src/metrics/config.ts +0 -495
- package/test/unit/spec/meeting/effectsState.js +0 -285
|
@@ -3,12 +3,14 @@ import sinon from 'sinon';
|
|
|
3
3
|
import {cloneDeep} from 'lodash';
|
|
4
4
|
import {assert} from '@webex/test-helper-chai';
|
|
5
5
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
6
|
+
import testUtils from '../../../utils/testUtils';
|
|
6
7
|
import Meetings from '@webex/plugin-meetings';
|
|
7
8
|
import LocusInfo from '@webex/plugin-meetings/src/locus-info';
|
|
8
9
|
import SelfUtils from '@webex/plugin-meetings/src/locus-info/selfUtils';
|
|
9
10
|
import InfoUtils from '@webex/plugin-meetings/src/locus-info/infoUtils';
|
|
10
11
|
import EmbeddedAppsUtils from '@webex/plugin-meetings/src/locus-info/embeddedAppsUtils';
|
|
11
12
|
import LocusDeltaParser from '@webex/plugin-meetings/src/locus-info/parser';
|
|
13
|
+
import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
12
14
|
|
|
13
15
|
import {
|
|
14
16
|
LOCUSINFO,
|
|
@@ -16,6 +18,10 @@ import {
|
|
|
16
18
|
LOCUSEVENT,
|
|
17
19
|
EVENTS,
|
|
18
20
|
DISPLAY_HINTS,
|
|
21
|
+
_CALL_,
|
|
22
|
+
LOCUS,
|
|
23
|
+
MEETING_STATE,
|
|
24
|
+
_MEETING_,
|
|
19
25
|
} from '../../../../src/constants';
|
|
20
26
|
|
|
21
27
|
import {self, selfWithInactivity} from './selfConstant';
|
|
@@ -33,6 +39,7 @@ describe('plugin-meetings', () => {
|
|
|
33
39
|
const locus = {};
|
|
34
40
|
const meetingId = 'meetingId';
|
|
35
41
|
let locusInfo;
|
|
42
|
+
let sendBehavioralMetricStub;
|
|
36
43
|
|
|
37
44
|
const webex = new MockWebex({
|
|
38
45
|
children: {
|
|
@@ -59,6 +66,12 @@ describe('plugin-meetings', () => {
|
|
|
59
66
|
},
|
|
60
67
|
},
|
|
61
68
|
};
|
|
69
|
+
|
|
70
|
+
sendBehavioralMetricStub = sinon.stub(Metrics, 'sendBehavioralMetric');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
sinon.restore();
|
|
62
75
|
});
|
|
63
76
|
|
|
64
77
|
describe('#updateControls', () => {
|
|
@@ -66,8 +79,12 @@ describe('plugin-meetings', () => {
|
|
|
66
79
|
|
|
67
80
|
beforeEach('setup new controls', () => {
|
|
68
81
|
newControls = {
|
|
82
|
+
disallowUnmute: {enabled: true},
|
|
69
83
|
lock: {},
|
|
70
84
|
meetingFull: {},
|
|
85
|
+
muteOnEntry: {enabled: true},
|
|
86
|
+
raiseHand: {enabled: true},
|
|
87
|
+
reactions: {enabled: true, showDisplayNameWithReactions: true},
|
|
71
88
|
record: {
|
|
72
89
|
recording: false,
|
|
73
90
|
paused: false,
|
|
@@ -76,12 +93,14 @@ describe('plugin-meetings', () => {
|
|
|
76
93
|
modifiedBy: 'George Kittle',
|
|
77
94
|
},
|
|
78
95
|
},
|
|
79
|
-
shareControl: {},
|
|
96
|
+
shareControl: {control: 'example-value'},
|
|
80
97
|
transcribe: {},
|
|
98
|
+
viewTheParticipantList: {enabled: true},
|
|
81
99
|
meetingContainer: {
|
|
82
100
|
meetingContainerUrl: 'http://new-url.com',
|
|
83
101
|
},
|
|
84
102
|
entryExitTone: {enabled: true, mode: 'foo'},
|
|
103
|
+
video: {enabled: true},
|
|
85
104
|
};
|
|
86
105
|
});
|
|
87
106
|
|
|
@@ -95,6 +114,97 @@ describe('plugin-meetings', () => {
|
|
|
95
114
|
assert.equal(locusInfo.controls, newControls);
|
|
96
115
|
});
|
|
97
116
|
|
|
117
|
+
it('should trigger the CONTROLS_MUTE_ON_ENTRY_CHANGED event when necessary', () => {
|
|
118
|
+
locusInfo.controls = {};
|
|
119
|
+
locusInfo.emitScoped = sinon.stub();
|
|
120
|
+
locusInfo.updateControls(newControls);
|
|
121
|
+
|
|
122
|
+
assert.calledWith(
|
|
123
|
+
locusInfo.emitScoped,
|
|
124
|
+
{file: 'locus-info', function: 'updateControls'},
|
|
125
|
+
LOCUSINFO.EVENTS.CONTROLS_MUTE_ON_ENTRY_CHANGED,
|
|
126
|
+
{state: newControls.muteOnEntry}
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should trigger the CONTROLS_SHARE_CONTROL_CHANGED event when necessary', () => {
|
|
131
|
+
locusInfo.controls = {};
|
|
132
|
+
locusInfo.emitScoped = sinon.stub();
|
|
133
|
+
locusInfo.updateControls(newControls);
|
|
134
|
+
|
|
135
|
+
assert.calledWith(
|
|
136
|
+
locusInfo.emitScoped,
|
|
137
|
+
{file: 'locus-info', function: 'updateControls'},
|
|
138
|
+
LOCUSINFO.EVENTS.CONTROLS_SHARE_CONTROL_CHANGED,
|
|
139
|
+
{state: newControls.shareControl}
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should trigger the CONTROLS_DISALLOW_UNMUTE_CHANGED event when necessary', () => {
|
|
144
|
+
locusInfo.controls = {};
|
|
145
|
+
locusInfo.emitScoped = sinon.stub();
|
|
146
|
+
locusInfo.updateControls(newControls);
|
|
147
|
+
|
|
148
|
+
assert.calledWith(
|
|
149
|
+
locusInfo.emitScoped,
|
|
150
|
+
{file: 'locus-info', function: 'updateControls'},
|
|
151
|
+
LOCUSINFO.EVENTS.CONTROLS_DISALLOW_UNMUTE_CHANGED,
|
|
152
|
+
{state: newControls.disallowUnmute}
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should trigger the CONTROLS_REACTIONS_CHANGED event when necessary', () => {
|
|
157
|
+
locusInfo.controls = {};
|
|
158
|
+
locusInfo.emitScoped = sinon.stub();
|
|
159
|
+
locusInfo.updateControls(newControls);
|
|
160
|
+
|
|
161
|
+
assert.calledWith(
|
|
162
|
+
locusInfo.emitScoped,
|
|
163
|
+
{file: 'locus-info', function: 'updateControls'},
|
|
164
|
+
LOCUSINFO.EVENTS.CONTROLS_REACTIONS_CHANGED,
|
|
165
|
+
{state: newControls.reactions}
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should trigger the CONTROLS_VIEW_THE_PARTICIPANTS_LIST_CHANGED event when necessary', () => {
|
|
170
|
+
locusInfo.controls = {};
|
|
171
|
+
locusInfo.emitScoped = sinon.stub();
|
|
172
|
+
locusInfo.updateControls(newControls);
|
|
173
|
+
|
|
174
|
+
assert.calledWith(
|
|
175
|
+
locusInfo.emitScoped,
|
|
176
|
+
{file: 'locus-info', function: 'updateControls'},
|
|
177
|
+
LOCUSINFO.EVENTS.CONTROLS_VIEW_THE_PARTICIPANTS_LIST_CHANGED,
|
|
178
|
+
{state: newControls.viewTheParticipantList}
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should trigger the CONTROLS_RAISE_HAND_CHANGED event when necessary', () => {
|
|
183
|
+
locusInfo.controls = {};
|
|
184
|
+
locusInfo.emitScoped = sinon.stub();
|
|
185
|
+
locusInfo.updateControls(newControls);
|
|
186
|
+
|
|
187
|
+
assert.calledWith(
|
|
188
|
+
locusInfo.emitScoped,
|
|
189
|
+
{file: 'locus-info', function: 'updateControls'},
|
|
190
|
+
LOCUSINFO.EVENTS.CONTROLS_RAISE_HAND_CHANGED,
|
|
191
|
+
{state: newControls.raiseHand}
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('should trigger the CONTROLS_VIDEO_CHANGED event when necessary', () => {
|
|
196
|
+
locusInfo.controls = {};
|
|
197
|
+
locusInfo.emitScoped = sinon.stub();
|
|
198
|
+
locusInfo.updateControls(newControls);
|
|
199
|
+
|
|
200
|
+
assert.calledWith(
|
|
201
|
+
locusInfo.emitScoped,
|
|
202
|
+
{file: 'locus-info', function: 'updateControls'},
|
|
203
|
+
LOCUSINFO.EVENTS.CONTROLS_VIDEO_CHANGED,
|
|
204
|
+
{state: newControls.video}
|
|
205
|
+
);
|
|
206
|
+
});
|
|
207
|
+
|
|
98
208
|
it('should not trigger the CONTROLS_RECORDING_UPDATED event', () => {
|
|
99
209
|
locusInfo.controls = {};
|
|
100
210
|
locusInfo.emitScoped = sinon.stub();
|
|
@@ -279,9 +389,11 @@ describe('plugin-meetings', () => {
|
|
|
279
389
|
|
|
280
390
|
it('should update the breakout state', () => {
|
|
281
391
|
locusInfo.emitScoped = sinon.stub();
|
|
282
|
-
|
|
392
|
+
let tmpStub = sinon.stub(SelfUtils, 'getReplacedBreakoutMoveId').returns('breakoutMoveId');
|
|
393
|
+
newControls.breakout = {breakout: {}};
|
|
394
|
+
let selfInfo = {};
|
|
283
395
|
|
|
284
|
-
locusInfo.updateControls(newControls);
|
|
396
|
+
locusInfo.updateControls(newControls, selfInfo);
|
|
285
397
|
|
|
286
398
|
assert.calledWith(
|
|
287
399
|
locusInfo.emitScoped,
|
|
@@ -291,7 +403,28 @@ describe('plugin-meetings', () => {
|
|
|
291
403
|
},
|
|
292
404
|
LOCUSINFO.EVENTS.CONTROLS_MEETING_BREAKOUT_UPDATED,
|
|
293
405
|
{
|
|
294
|
-
breakout:
|
|
406
|
+
breakout: newControls.breakout,
|
|
407
|
+
}
|
|
408
|
+
);
|
|
409
|
+
tmpStub.restore();
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('should update the interpretation state', () => {
|
|
413
|
+
locusInfo.emitScoped = sinon.stub();
|
|
414
|
+
newControls.interpretation = {siLanguages: [{languageCode: 20, languageName: 'en'}]};
|
|
415
|
+
let selfInfo = {};
|
|
416
|
+
|
|
417
|
+
locusInfo.updateControls(newControls, selfInfo);
|
|
418
|
+
|
|
419
|
+
assert.calledWith(
|
|
420
|
+
locusInfo.emitScoped,
|
|
421
|
+
{
|
|
422
|
+
file: 'locus-info',
|
|
423
|
+
function: 'updateControls',
|
|
424
|
+
},
|
|
425
|
+
LOCUSINFO.EVENTS.CONTROLS_MEETING_INTERPRETATION_UPDATED,
|
|
426
|
+
{
|
|
427
|
+
interpretation: newControls.interpretation,
|
|
295
428
|
}
|
|
296
429
|
);
|
|
297
430
|
});
|
|
@@ -417,6 +550,39 @@ describe('plugin-meetings', () => {
|
|
|
417
550
|
assert.notEqual(x.args[1], LOCUSINFO.EVENTS.CONTROLS_ENTRY_EXIT_TONE_UPDATED);
|
|
418
551
|
});
|
|
419
552
|
});
|
|
553
|
+
|
|
554
|
+
it('should update videoEnabled when changed', () => {
|
|
555
|
+
locusInfo.controls = {};
|
|
556
|
+
|
|
557
|
+
locusInfo.emitScoped = sinon.stub();
|
|
558
|
+
locusInfo.updateControls(newControls);
|
|
559
|
+
|
|
560
|
+
assert.calledWith(
|
|
561
|
+
locusInfo.emitScoped,
|
|
562
|
+
{
|
|
563
|
+
file: 'locus-info',
|
|
564
|
+
function: 'updateControls',
|
|
565
|
+
},
|
|
566
|
+
LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED,
|
|
567
|
+
{unmuteAllowed: true}
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
assert.equal(mockMeeting.unmuteVideoAllowed, true);
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
it('should not update videoEnabled when unchanged', () => {
|
|
574
|
+
locusInfo.controls = {videoEnabled: true};
|
|
575
|
+
|
|
576
|
+
locusInfo.emitScoped = sinon.stub();
|
|
577
|
+
locusInfo.updateControls(newControls);
|
|
578
|
+
|
|
579
|
+
locusInfo.emitScoped.getCalls().forEach((x) => {
|
|
580
|
+
// check that no calls in emitScoped are for SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED
|
|
581
|
+
assert.notEqual(x.args[1], LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED);
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
assert.equal(mockMeeting.unmuteVideoAllowed, undefined);
|
|
585
|
+
});
|
|
420
586
|
});
|
|
421
587
|
|
|
422
588
|
describe('#updateParticipants()', () => {
|
|
@@ -470,6 +636,7 @@ describe('plugin-meetings', () => {
|
|
|
470
636
|
selfIdentity: '123',
|
|
471
637
|
selfId: '2',
|
|
472
638
|
hostId: '3',
|
|
639
|
+
isReplace: undefined,
|
|
473
640
|
}
|
|
474
641
|
);
|
|
475
642
|
// note: in a real use case, recordingId, selfId, and hostId would all be the same
|
|
@@ -477,6 +644,43 @@ describe('plugin-meetings', () => {
|
|
|
477
644
|
// are being correctly grabbed from locusInfo.parsedLocus within updateParticipants
|
|
478
645
|
});
|
|
479
646
|
|
|
647
|
+
it('should call with breakout control info', () => {
|
|
648
|
+
locusInfo.parsedLocus = {
|
|
649
|
+
controls: {
|
|
650
|
+
record: {
|
|
651
|
+
modifiedBy: '1',
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
self: {
|
|
655
|
+
selfIdentity: '123',
|
|
656
|
+
selfId: '2',
|
|
657
|
+
},
|
|
658
|
+
host: {
|
|
659
|
+
hostId: '3',
|
|
660
|
+
},
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
locusInfo.emitScoped = sinon.stub();
|
|
664
|
+
locusInfo.updateParticipants({}, true);
|
|
665
|
+
|
|
666
|
+
assert.calledWith(
|
|
667
|
+
locusInfo.emitScoped,
|
|
668
|
+
{
|
|
669
|
+
file: 'locus-info',
|
|
670
|
+
function: 'updateParticipants',
|
|
671
|
+
},
|
|
672
|
+
EVENTS.LOCUS_INFO_UPDATE_PARTICIPANTS,
|
|
673
|
+
{
|
|
674
|
+
participants: {},
|
|
675
|
+
recordingId: '1',
|
|
676
|
+
selfIdentity: '123',
|
|
677
|
+
selfId: '2',
|
|
678
|
+
hostId: '3',
|
|
679
|
+
isReplace: true,
|
|
680
|
+
}
|
|
681
|
+
);
|
|
682
|
+
});
|
|
683
|
+
|
|
480
684
|
it('should update the deltaParticipants object', () => {
|
|
481
685
|
const prev = locusInfo.deltaParticipants;
|
|
482
686
|
|
|
@@ -683,6 +887,83 @@ describe('plugin-meetings', () => {
|
|
|
683
887
|
);
|
|
684
888
|
});
|
|
685
889
|
|
|
890
|
+
describe('SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED', () => {
|
|
891
|
+
it('should emit event when video muted on entry', () => {
|
|
892
|
+
// usually "previous self" is just undefined when we get first self from locus
|
|
893
|
+
locusInfo.self = undefined;
|
|
894
|
+
const selfWithMutedByOthers = cloneDeep(self);
|
|
895
|
+
|
|
896
|
+
// remoteVideoMuted
|
|
897
|
+
selfWithMutedByOthers.controls.video.muted = true;
|
|
898
|
+
|
|
899
|
+
locusInfo.webex.internal.device.url = self.deviceUrl;
|
|
900
|
+
locusInfo.emitScoped = sinon.stub();
|
|
901
|
+
locusInfo.updateSelf(selfWithMutedByOthers, []);
|
|
902
|
+
|
|
903
|
+
assert.calledWith(
|
|
904
|
+
locusInfo.emitScoped,
|
|
905
|
+
{
|
|
906
|
+
file: 'locus-info',
|
|
907
|
+
function: 'updateSelf',
|
|
908
|
+
},
|
|
909
|
+
LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED,
|
|
910
|
+
{muted: true}
|
|
911
|
+
);
|
|
912
|
+
|
|
913
|
+
// but sometimes "previous self" is defined, but without controls.audio.muted, so we test this here:
|
|
914
|
+
locusInfo.self = cloneDeep(self);
|
|
915
|
+
locusInfo.self.controls.video = {};
|
|
916
|
+
|
|
917
|
+
locusInfo.updateSelf(selfWithMutedByOthers, []);
|
|
918
|
+
assert.calledWith(
|
|
919
|
+
locusInfo.emitScoped,
|
|
920
|
+
{
|
|
921
|
+
file: 'locus-info',
|
|
922
|
+
function: 'updateSelf',
|
|
923
|
+
},
|
|
924
|
+
LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED,
|
|
925
|
+
{muted: true}
|
|
926
|
+
);
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
it('should not emit event when not muted on entry', () => {
|
|
930
|
+
locusInfo.self = undefined;
|
|
931
|
+
const selfWithMutedByOthersFalse = cloneDeep(self);
|
|
932
|
+
|
|
933
|
+
selfWithMutedByOthersFalse.controls.video.muted = false;
|
|
934
|
+
|
|
935
|
+
locusInfo.webex.internal.device.url = self.deviceUrl;
|
|
936
|
+
locusInfo.emitScoped = sinon.stub();
|
|
937
|
+
locusInfo.updateSelf(selfWithMutedByOthersFalse, []);
|
|
938
|
+
|
|
939
|
+
// we might get some calls to emitScoped, but we need to check that none of them are for SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED
|
|
940
|
+
locusInfo.emitScoped.getCalls().forEach((x) => {
|
|
941
|
+
assert.notEqual(x.args[1], LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED);
|
|
942
|
+
});
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
it('should emit event when remoteVideoMuted changed', () => {
|
|
946
|
+
locusInfo.self = self;
|
|
947
|
+
const selfWithMutedByOthers = cloneDeep(self);
|
|
948
|
+
|
|
949
|
+
selfWithMutedByOthers.controls.video.muted = true;
|
|
950
|
+
|
|
951
|
+
locusInfo.webex.internal.device.url = self.deviceUrl;
|
|
952
|
+
locusInfo.emitScoped = sinon.stub();
|
|
953
|
+
locusInfo.updateSelf(selfWithMutedByOthers, []);
|
|
954
|
+
|
|
955
|
+
assert.calledWith(
|
|
956
|
+
locusInfo.emitScoped,
|
|
957
|
+
{
|
|
958
|
+
file: 'locus-info',
|
|
959
|
+
function: 'updateSelf',
|
|
960
|
+
},
|
|
961
|
+
LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED,
|
|
962
|
+
{muted: true}
|
|
963
|
+
);
|
|
964
|
+
});
|
|
965
|
+
});
|
|
966
|
+
|
|
686
967
|
it('should trigger SELF_MEETING_BREAKOUTS_CHANGED when breakouts changed', () => {
|
|
687
968
|
locusInfo.self = self;
|
|
688
969
|
const selfWithBreakoutsChanged = cloneDeep(self);
|
|
@@ -701,19 +982,23 @@ describe('plugin-meetings', () => {
|
|
|
701
982
|
LOCUSINFO.EVENTS.SELF_MEETING_BREAKOUTS_CHANGED,
|
|
702
983
|
{
|
|
703
984
|
breakoutSessions: {
|
|
704
|
-
active: [
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
985
|
+
active: [
|
|
986
|
+
{
|
|
987
|
+
name: 'new name',
|
|
988
|
+
groupId: '0e73abb8-5584-49d8-be8d-806d2a8247ca',
|
|
989
|
+
sessionId: '1cf41ab1-2e57-4d95-b7e9-5613acddfb0f',
|
|
990
|
+
sessionType: 'BREAKOUT',
|
|
991
|
+
},
|
|
992
|
+
],
|
|
993
|
+
allowed: [
|
|
994
|
+
{
|
|
995
|
+
name: 'Breakout session 2',
|
|
996
|
+
groupId: '0e73abb8-5584-49d8-be8d-806d2a8247ca',
|
|
997
|
+
sessionId: '1cf41ab1-2e57-4d95-b7e9-5613acddfb0f',
|
|
998
|
+
sessionType: 'BREAKOUT',
|
|
999
|
+
},
|
|
1000
|
+
],
|
|
1001
|
+
},
|
|
717
1002
|
}
|
|
718
1003
|
);
|
|
719
1004
|
});
|
|
@@ -789,6 +1074,8 @@ describe('plugin-meetings', () => {
|
|
|
789
1074
|
const selfWithRequestedToUnmute = cloneDeep(self);
|
|
790
1075
|
|
|
791
1076
|
selfWithRequestedToUnmute.controls.audio.requestedToUnmute = true;
|
|
1077
|
+
selfWithRequestedToUnmute.controls.audio.lastModifiedRequestedToUnmute =
|
|
1078
|
+
'2023-06-16T19:25:04.369Z';
|
|
792
1079
|
|
|
793
1080
|
locusInfo.webex.internal.device.url = self.deviceUrl;
|
|
794
1081
|
locusInfo.emitScoped = sinon.stub();
|
|
@@ -943,6 +1230,88 @@ describe('plugin-meetings', () => {
|
|
|
943
1230
|
{isSharingBlocked: true}
|
|
944
1231
|
);
|
|
945
1232
|
});
|
|
1233
|
+
|
|
1234
|
+
it('should trigger SELF_ROLES_CHANGED if self roles changed', () => {
|
|
1235
|
+
locusInfo.self = self;
|
|
1236
|
+
locusInfo.emitScoped = sinon.stub();
|
|
1237
|
+
const sampleNewSelf = cloneDeep(self);
|
|
1238
|
+
sampleNewSelf.controls.role.roles = [{type: 'COHOST', hasRole: true}];
|
|
1239
|
+
|
|
1240
|
+
locusInfo.updateSelf(sampleNewSelf, []);
|
|
1241
|
+
|
|
1242
|
+
assert.calledWith(
|
|
1243
|
+
locusInfo.emitScoped,
|
|
1244
|
+
{
|
|
1245
|
+
file: 'locus-info',
|
|
1246
|
+
function: 'updateSelf',
|
|
1247
|
+
},
|
|
1248
|
+
LOCUSINFO.EVENTS.SELF_ROLES_CHANGED,
|
|
1249
|
+
{oldRoles: ['PRESENTER'], newRoles: ['COHOST']}
|
|
1250
|
+
);
|
|
1251
|
+
});
|
|
1252
|
+
|
|
1253
|
+
it('should not trigger SELF_ROLES_CHANGED if self roles not changed', () => {
|
|
1254
|
+
locusInfo.self = self;
|
|
1255
|
+
locusInfo.emitScoped = sinon.stub();
|
|
1256
|
+
const sampleNewSelf = cloneDeep(self);
|
|
1257
|
+
sampleNewSelf.controls.role.roles = [{type: 'PRESENTER', hasRole: true}];
|
|
1258
|
+
|
|
1259
|
+
locusInfo.updateSelf(sampleNewSelf, []);
|
|
1260
|
+
|
|
1261
|
+
assert.neverCalledWith(
|
|
1262
|
+
locusInfo.emitScoped,
|
|
1263
|
+
{
|
|
1264
|
+
file: 'locus-info',
|
|
1265
|
+
function: 'updateSelf',
|
|
1266
|
+
},
|
|
1267
|
+
LOCUSINFO.EVENTS.SELF_ROLES_CHANGED,
|
|
1268
|
+
{oldRoles: ['PRESENTER'], newRoles: ['PRESENTER']}
|
|
1269
|
+
);
|
|
1270
|
+
});
|
|
1271
|
+
|
|
1272
|
+
it('should trigger SELF_MEETING_INTERPRETATION_CHANGED if self interpretation info changed', () => {
|
|
1273
|
+
locusInfo.self = self;
|
|
1274
|
+
locusInfo.emitScoped = sinon.stub();
|
|
1275
|
+
const sampleNewSelf = cloneDeep(self);
|
|
1276
|
+
sampleNewSelf.controls.interpretation.targetLanguage = 'it';
|
|
1277
|
+
|
|
1278
|
+
locusInfo.updateSelf(sampleNewSelf, []);
|
|
1279
|
+
|
|
1280
|
+
assert.calledWith(
|
|
1281
|
+
locusInfo.emitScoped,
|
|
1282
|
+
{
|
|
1283
|
+
file: 'locus-info',
|
|
1284
|
+
function: 'updateSelf',
|
|
1285
|
+
},
|
|
1286
|
+
LOCUSINFO.EVENTS.SELF_MEETING_INTERPRETATION_CHANGED,
|
|
1287
|
+
{
|
|
1288
|
+
interpretation: sampleNewSelf.controls.interpretation,
|
|
1289
|
+
selfParticipantId: self.id,
|
|
1290
|
+
}
|
|
1291
|
+
);
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
it('should not trigger SELF_MEETING_INTERPRETATION_CHANGED if self interpretation info not changed', () => {
|
|
1295
|
+
locusInfo.self = self;
|
|
1296
|
+
locusInfo.emitScoped = sinon.stub();
|
|
1297
|
+
const sampleNewSelf = cloneDeep(self);
|
|
1298
|
+
sampleNewSelf.controls.interpretation.targetLanguage = 'cn'; // same with previous one
|
|
1299
|
+
|
|
1300
|
+
locusInfo.updateSelf(sampleNewSelf, []);
|
|
1301
|
+
|
|
1302
|
+
assert.neverCalledWith(
|
|
1303
|
+
locusInfo.emitScoped,
|
|
1304
|
+
{
|
|
1305
|
+
file: 'locus-info',
|
|
1306
|
+
function: 'updateSelf',
|
|
1307
|
+
},
|
|
1308
|
+
LOCUSINFO.EVENTS.SELF_MEETING_INTERPRETATION_CHANGED,
|
|
1309
|
+
{
|
|
1310
|
+
interpretation: sampleNewSelf.controls.interpretation,
|
|
1311
|
+
selfParticipantId: self.id,
|
|
1312
|
+
}
|
|
1313
|
+
);
|
|
1314
|
+
});
|
|
946
1315
|
});
|
|
947
1316
|
|
|
948
1317
|
describe('#updateMeetingInfo', () => {
|
|
@@ -1016,7 +1385,6 @@ describe('plugin-meetings', () => {
|
|
|
1016
1385
|
function: 'updateMeetingInfo',
|
|
1017
1386
|
},
|
|
1018
1387
|
LOCUSINFO.EVENTS.MEETING_INFO_UPDATED,
|
|
1019
|
-
{info: locusInfo.parsedLocus.info, self},
|
|
1020
1388
|
];
|
|
1021
1389
|
|
|
1022
1390
|
if (expected) {
|
|
@@ -1027,15 +1395,58 @@ describe('plugin-meetings', () => {
|
|
|
1027
1395
|
locusInfo.emitScoped.resetHistory();
|
|
1028
1396
|
};
|
|
1029
1397
|
|
|
1030
|
-
|
|
1398
|
+
const checkMeetingInfoUpdatedCalledForRoles = (expected) => {
|
|
1399
|
+
const expectedArgs = [
|
|
1400
|
+
locusInfo.emitScoped,
|
|
1401
|
+
{
|
|
1402
|
+
file: 'locus-info',
|
|
1403
|
+
function: 'updateMeetingInfo',
|
|
1404
|
+
},
|
|
1405
|
+
LOCUSINFO.EVENTS.MEETING_INFO_UPDATED,
|
|
1406
|
+
];
|
|
1407
|
+
|
|
1408
|
+
if (expected) {
|
|
1409
|
+
assert.calledWith(...expectedArgs);
|
|
1410
|
+
} else {
|
|
1411
|
+
assert.neverCalledWith(...expectedArgs);
|
|
1412
|
+
}
|
|
1413
|
+
locusInfo.emitScoped.resetHistory();
|
|
1414
|
+
};
|
|
1415
|
+
|
|
1416
|
+
it('emits MEETING_INFO_UPDATED and updates the meeting if the info changes', () => {
|
|
1031
1417
|
const initialInfo = cloneDeep(meetingInfo);
|
|
1032
1418
|
|
|
1033
|
-
|
|
1419
|
+
let expectedMeeting;
|
|
1420
|
+
|
|
1421
|
+
/*
|
|
1422
|
+
When the event is triggered, it is required that the meeting has already
|
|
1423
|
+
been updated. This is why the meeting is being checked within the stubbed event emitter
|
|
1424
|
+
*/
|
|
1425
|
+
sinon.stub(locusInfo, 'emitScoped').callsFake(() => {
|
|
1426
|
+
assert.deepEqual(mockMeeting, expectedMeeting);
|
|
1427
|
+
})
|
|
1428
|
+
|
|
1034
1429
|
|
|
1035
1430
|
// set the info initially as locusInfo.info starts as undefined
|
|
1431
|
+
expectedMeeting = {
|
|
1432
|
+
coHost: {
|
|
1433
|
+
LOWER_SOMEONE_ELSES_HAND: true,
|
|
1434
|
+
},
|
|
1435
|
+
isLocked: false,
|
|
1436
|
+
isUnlocked: true,
|
|
1437
|
+
moderator: {
|
|
1438
|
+
LOWER_SOMEONE_ELSES_HAND: true,
|
|
1439
|
+
},
|
|
1440
|
+
policy: {
|
|
1441
|
+
LOCK_STATUS_UNLOCKED: true,
|
|
1442
|
+
ROSTER_IN_MEETING: true,
|
|
1443
|
+
},
|
|
1444
|
+
userDisplayHints: ['ROSTER_IN_MEETING', 'LOCK_STATUS_UNLOCKED'],
|
|
1445
|
+
};
|
|
1036
1446
|
locusInfo.updateMeetingInfo(initialInfo, self);
|
|
1037
1447
|
|
|
1038
1448
|
// since it was initially undefined, this should trigger the event
|
|
1449
|
+
|
|
1039
1450
|
checkMeetingInfoUpdatedCalled(true);
|
|
1040
1451
|
|
|
1041
1452
|
const newInfo = cloneDeep(meetingInfo);
|
|
@@ -1043,16 +1454,79 @@ describe('plugin-meetings', () => {
|
|
|
1043
1454
|
newInfo.displayHints.coHost = [DISPLAY_HINTS.LOCK_CONTROL_LOCK];
|
|
1044
1455
|
|
|
1045
1456
|
// Updating with different info should trigger the event
|
|
1457
|
+
expectedMeeting = {
|
|
1458
|
+
coHost: {
|
|
1459
|
+
LOWER_SOMEONE_ELSES_HAND: true,
|
|
1460
|
+
LOCK_CONTROL_LOCK: true,
|
|
1461
|
+
},
|
|
1462
|
+
isLocked: false,
|
|
1463
|
+
isUnlocked: true,
|
|
1464
|
+
moderator: {
|
|
1465
|
+
LOWER_SOMEONE_ELSES_HAND: true,
|
|
1466
|
+
},
|
|
1467
|
+
policy: {
|
|
1468
|
+
LOCK_STATUS_UNLOCKED: true,
|
|
1469
|
+
ROSTER_IN_MEETING: true,
|
|
1470
|
+
},
|
|
1471
|
+
userDisplayHints: ['ROSTER_IN_MEETING', 'LOCK_STATUS_UNLOCKED'],
|
|
1472
|
+
};
|
|
1046
1473
|
locusInfo.updateMeetingInfo(newInfo, self);
|
|
1047
1474
|
|
|
1048
1475
|
checkMeetingInfoUpdatedCalled(true);
|
|
1049
1476
|
|
|
1050
1477
|
// update it with the same info
|
|
1478
|
+
expectedMeeting = {
|
|
1479
|
+
coHost: {
|
|
1480
|
+
LOWER_SOMEONE_ELSES_HAND: true,
|
|
1481
|
+
LOCK_CONTROL_LOCK: true,
|
|
1482
|
+
},
|
|
1483
|
+
isLocked: false,
|
|
1484
|
+
isUnlocked: true,
|
|
1485
|
+
moderator: {
|
|
1486
|
+
LOWER_SOMEONE_ELSES_HAND: true,
|
|
1487
|
+
},
|
|
1488
|
+
policy: {
|
|
1489
|
+
LOCK_STATUS_UNLOCKED: true,
|
|
1490
|
+
ROSTER_IN_MEETING: true,
|
|
1491
|
+
},
|
|
1492
|
+
userDisplayHints: ['ROSTER_IN_MEETING', 'LOCK_STATUS_UNLOCKED'],
|
|
1493
|
+
};
|
|
1051
1494
|
locusInfo.updateMeetingInfo(newInfo, self);
|
|
1052
1495
|
|
|
1053
1496
|
// since the info is the same it should not call trigger the event
|
|
1054
1497
|
checkMeetingInfoUpdatedCalled(false);
|
|
1055
|
-
|
|
1498
|
+
|
|
1499
|
+
// update it with the same info, but roles changed
|
|
1500
|
+
const updateSelf = cloneDeep(self);
|
|
1501
|
+
updateSelf?.controls?.role?.roles.push({
|
|
1502
|
+
type: 'COHOST',
|
|
1503
|
+
hasRole: true,
|
|
1504
|
+
});
|
|
1505
|
+
expectedMeeting = {
|
|
1506
|
+
coHost: {
|
|
1507
|
+
LOWER_SOMEONE_ELSES_HAND: true,
|
|
1508
|
+
LOCK_CONTROL_LOCK: true,
|
|
1509
|
+
},
|
|
1510
|
+
isLocked: false,
|
|
1511
|
+
isUnlocked: true,
|
|
1512
|
+
moderator: {
|
|
1513
|
+
LOWER_SOMEONE_ELSES_HAND: true,
|
|
1514
|
+
},
|
|
1515
|
+
policy: {
|
|
1516
|
+
LOCK_STATUS_UNLOCKED: true,
|
|
1517
|
+
ROSTER_IN_MEETING: true,
|
|
1518
|
+
},
|
|
1519
|
+
userDisplayHints: [
|
|
1520
|
+
'ROSTER_IN_MEETING',
|
|
1521
|
+
'LOCK_STATUS_UNLOCKED',
|
|
1522
|
+
'LOCK_CONTROL_LOCK',
|
|
1523
|
+
'LOWER_SOMEONE_ELSES_HAND',
|
|
1524
|
+
],
|
|
1525
|
+
};
|
|
1526
|
+
locusInfo.updateMeetingInfo(newInfo, updateSelf);
|
|
1527
|
+
// since the info is the same but roles changed, it should call trigger the event
|
|
1528
|
+
checkMeetingInfoUpdatedCalledForRoles(true);
|
|
1529
|
+
});
|
|
1056
1530
|
|
|
1057
1531
|
it('gets roles from self if available', () => {
|
|
1058
1532
|
const initialInfo = cloneDeep(meetingInfo);
|
|
@@ -1157,6 +1631,8 @@ describe('plugin-meetings', () => {
|
|
|
1157
1631
|
fakeLocus = {
|
|
1158
1632
|
meeting: true,
|
|
1159
1633
|
participants: true,
|
|
1634
|
+
url: 'newLocusUrl',
|
|
1635
|
+
syncUrl: 'newSyncUrl',
|
|
1160
1636
|
};
|
|
1161
1637
|
});
|
|
1162
1638
|
|
|
@@ -1205,8 +1681,8 @@ describe('plugin-meetings', () => {
|
|
|
1205
1681
|
const newLocus = {
|
|
1206
1682
|
self: {
|
|
1207
1683
|
reason: 'MOVED',
|
|
1208
|
-
state: 'LEFT'
|
|
1209
|
-
}
|
|
1684
|
+
state: 'LEFT',
|
|
1685
|
+
},
|
|
1210
1686
|
};
|
|
1211
1687
|
|
|
1212
1688
|
locusInfo.updateControls = sinon.stub();
|
|
@@ -1258,12 +1734,39 @@ describe('plugin-meetings', () => {
|
|
|
1258
1734
|
sandbox.stub(locusInfo, 'updateParticipants');
|
|
1259
1735
|
sandbox.stub(locusInfo, 'isMeetingActive');
|
|
1260
1736
|
sandbox.stub(locusInfo, 'handleOneOnOneEvent');
|
|
1737
|
+
sandbox.stub(locusParser, 'isNewFullLocus').returns(true);
|
|
1261
1738
|
|
|
1262
1739
|
locusInfo.onFullLocus(fakeLocus, eventType);
|
|
1263
1740
|
|
|
1264
1741
|
assert.equal(fakeLocus, locusParser.workingCopy);
|
|
1265
1742
|
});
|
|
1266
1743
|
|
|
1744
|
+
it('onFullLocus() does not do anything if the incoming full locus DTO is old', () => {
|
|
1745
|
+
const eventType = 'fakeEvent';
|
|
1746
|
+
|
|
1747
|
+
locusParser.workingCopy = {};
|
|
1748
|
+
|
|
1749
|
+
const oldWorkingCopy = locusParser.workingCopy;
|
|
1750
|
+
|
|
1751
|
+
const spies = [
|
|
1752
|
+
sandbox.stub(locusInfo, 'updateParticipantDeltas'),
|
|
1753
|
+
sandbox.stub(locusInfo, 'updateLocusInfo'),
|
|
1754
|
+
sandbox.stub(locusInfo, 'updateParticipants'),
|
|
1755
|
+
sandbox.stub(locusInfo, 'isMeetingActive'),
|
|
1756
|
+
sandbox.stub(locusInfo, 'handleOneOnOneEvent'),
|
|
1757
|
+
];
|
|
1758
|
+
|
|
1759
|
+
sandbox.stub(locusParser, 'isNewFullLocus').returns(false);
|
|
1760
|
+
|
|
1761
|
+
locusInfo.onFullLocus(fakeLocus, eventType);
|
|
1762
|
+
|
|
1763
|
+
spies.forEach((spy) => {
|
|
1764
|
+
assert.notCalled(spy);
|
|
1765
|
+
});
|
|
1766
|
+
|
|
1767
|
+
assert.equal(oldWorkingCopy, locusParser.workingCopy);
|
|
1768
|
+
});
|
|
1769
|
+
|
|
1267
1770
|
it('onDeltaAction applies locus delta data to meeting', () => {
|
|
1268
1771
|
const action = 'fake action';
|
|
1269
1772
|
const parsedLoci = 'fake loci';
|
|
@@ -1290,33 +1793,82 @@ describe('plugin-meetings', () => {
|
|
|
1290
1793
|
assert.calledWith(meeting.locusInfo.onDeltaLocus, fakeLocus);
|
|
1291
1794
|
});
|
|
1292
1795
|
|
|
1293
|
-
it('applyLocusDeltaData gets
|
|
1796
|
+
it('applyLocusDeltaData gets delta locus on DESYNC action if we have a syncUrl', () => {
|
|
1797
|
+
const {DESYNC} = LocusDeltaParser.loci;
|
|
1798
|
+
const fakeDeltaLocus = {id: 'fake delta locus'};
|
|
1799
|
+
const meeting = {
|
|
1800
|
+
meetingRequest: {
|
|
1801
|
+
getLocusDTO: sandbox.stub().resolves({body: fakeDeltaLocus}),
|
|
1802
|
+
},
|
|
1803
|
+
locusInfo: {
|
|
1804
|
+
handleLocusDelta: sandbox.stub(),
|
|
1805
|
+
},
|
|
1806
|
+
locusUrl: 'oldLocusUrl',
|
|
1807
|
+
};
|
|
1808
|
+
|
|
1809
|
+
locusInfo.locusParser.workingCopy = {
|
|
1810
|
+
syncUrl: 'oldSyncUrl',
|
|
1811
|
+
};
|
|
1812
|
+
|
|
1813
|
+
// Since we have a promise inside a function we want to test that's not returned,
|
|
1814
|
+
// we will wait and stub it's last function to resolve this waiting promise.
|
|
1815
|
+
// Also ensures .handleLocusDelta() is called before .resume()
|
|
1816
|
+
return new Promise((resolve) => {
|
|
1817
|
+
locusInfo.locusParser.resume = sandbox.stub().callsFake(() => resolve());
|
|
1818
|
+
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
|
1819
|
+
}).then(() => {
|
|
1820
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'oldSyncUrl'});
|
|
1821
|
+
|
|
1822
|
+
assert.calledOnceWithExactly(meeting.locusInfo.handleLocusDelta, fakeDeltaLocus, meeting);
|
|
1823
|
+
assert.calledOnce(locusInfo.locusParser.resume);
|
|
1824
|
+
});
|
|
1825
|
+
});
|
|
1826
|
+
|
|
1827
|
+
it('applyLocusDeltaData gets delta locus on DESYNC action if we have a syncUrl (empty response body)', () => {
|
|
1294
1828
|
const {DESYNC} = LocusDeltaParser.loci;
|
|
1295
1829
|
const meeting = {
|
|
1296
1830
|
meetingRequest: {
|
|
1297
|
-
|
|
1831
|
+
getLocusDTO: sandbox.stub().resolves({body: {}}),
|
|
1298
1832
|
},
|
|
1299
1833
|
locusInfo: {
|
|
1834
|
+
handleLocusDelta: sandbox.stub(),
|
|
1300
1835
|
onFullLocus: sandbox.stub(),
|
|
1301
1836
|
},
|
|
1837
|
+
locusUrl: 'oldLocusUrl',
|
|
1302
1838
|
};
|
|
1303
1839
|
|
|
1304
|
-
locusInfo.locusParser.
|
|
1305
|
-
|
|
1840
|
+
locusInfo.locusParser.workingCopy = {
|
|
1841
|
+
syncUrl: 'oldSyncUrl',
|
|
1842
|
+
};
|
|
1306
1843
|
|
|
1307
|
-
|
|
1844
|
+
// Since we have a promise inside a function we want to test that's not returned,
|
|
1845
|
+
// we will wait and stub it's last function to resolve this waiting promise.
|
|
1846
|
+
return new Promise((resolve) => {
|
|
1847
|
+
locusInfo.locusParser.resume = sandbox.stub().callsFake(() => resolve());
|
|
1848
|
+
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
|
1849
|
+
}).then(() => {
|
|
1850
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'oldSyncUrl'});
|
|
1851
|
+
|
|
1852
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
|
1853
|
+
assert.notCalled(meeting.locusInfo.onFullLocus);
|
|
1854
|
+
assert.calledOnce(locusInfo.locusParser.resume);
|
|
1855
|
+
});
|
|
1308
1856
|
});
|
|
1309
1857
|
|
|
1310
|
-
it('
|
|
1858
|
+
it('applyLocusDeltaData gets full locus on DESYNC action if we do not have a syncUrl', () => {
|
|
1311
1859
|
const {DESYNC} = LocusDeltaParser.loci;
|
|
1860
|
+
const fakeFullLocusDto = {id: 'fake full locus dto'};
|
|
1312
1861
|
const meeting = {
|
|
1313
1862
|
meetingRequest: {
|
|
1314
|
-
|
|
1863
|
+
getLocusDTO: sandbox.stub().resolves({body: fakeFullLocusDto}),
|
|
1864
|
+
},
|
|
1865
|
+
locusInfo: {
|
|
1866
|
+
onFullLocus: sandbox.stub(),
|
|
1315
1867
|
},
|
|
1316
|
-
|
|
1868
|
+
locusUrl: 'oldLocusUrl',
|
|
1317
1869
|
};
|
|
1318
1870
|
|
|
1319
|
-
locusInfo.
|
|
1871
|
+
locusInfo.locusParser.workingCopy = {}; // no syncUrl
|
|
1320
1872
|
|
|
1321
1873
|
// Since we have a promise inside a function we want to test that's not returned,
|
|
1322
1874
|
// we will wait and stub it's last function to resolve this waiting promise.
|
|
@@ -1325,10 +1877,330 @@ describe('plugin-meetings', () => {
|
|
|
1325
1877
|
locusInfo.locusParser.resume = sandbox.stub().callsFake(() => resolve());
|
|
1326
1878
|
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
|
1327
1879
|
}).then(() => {
|
|
1328
|
-
assert.
|
|
1880
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'oldLocusUrl'});
|
|
1881
|
+
|
|
1882
|
+
assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
|
|
1329
1883
|
assert.calledOnce(locusInfo.locusParser.resume);
|
|
1330
1884
|
});
|
|
1331
1885
|
});
|
|
1886
|
+
|
|
1887
|
+
it('applyLocusDeltaData handles LOCUS_URL_CHANGED action correctly', () => {
|
|
1888
|
+
const {LOCUS_URL_CHANGED} = LocusDeltaParser.loci;
|
|
1889
|
+
const fakeDeltaLocus = {id: 'fake delta locus'};
|
|
1890
|
+
const meeting = {
|
|
1891
|
+
meetingRequest: {
|
|
1892
|
+
getLocusDTO: sandbox.stub().resolves({body: fakeDeltaLocus}),
|
|
1893
|
+
},
|
|
1894
|
+
locusInfo: {
|
|
1895
|
+
handleLocusDelta: sandbox.stub(),
|
|
1896
|
+
},
|
|
1897
|
+
locusUrl: 'current locus url',
|
|
1898
|
+
};
|
|
1899
|
+
|
|
1900
|
+
locusInfo.locusParser.workingCopy = {
|
|
1901
|
+
syncUrl: 'current sync url',
|
|
1902
|
+
};
|
|
1903
|
+
|
|
1904
|
+
locusInfo.applyLocusDeltaData(LOCUS_URL_CHANGED, fakeLocus, meeting);
|
|
1905
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'current sync url'});
|
|
1906
|
+
});
|
|
1907
|
+
|
|
1908
|
+
describe('edge cases for sync failing', () => {
|
|
1909
|
+
const {DESYNC} = LocusDeltaParser.loci;
|
|
1910
|
+
const fakeFullLocusDto = {id: 'fake full locus dto'};
|
|
1911
|
+
let meeting;
|
|
1912
|
+
|
|
1913
|
+
beforeEach(() => {
|
|
1914
|
+
sinon.stub(locusInfo.locusParser, 'resume');
|
|
1915
|
+
sinon.stub(webex.meetings, 'destroy');
|
|
1916
|
+
|
|
1917
|
+
meeting = {
|
|
1918
|
+
meetingRequest: {
|
|
1919
|
+
getLocusDTO: sandbox.stub(),
|
|
1920
|
+
},
|
|
1921
|
+
locusInfo: {
|
|
1922
|
+
handleLocusDelta: sandbox.stub(),
|
|
1923
|
+
onFullLocus: sandbox.stub(),
|
|
1924
|
+
},
|
|
1925
|
+
locusUrl: 'fullSyncUrl',
|
|
1926
|
+
};
|
|
1927
|
+
|
|
1928
|
+
locusInfo.locusParser.workingCopy = {
|
|
1929
|
+
syncUrl: 'deltaSyncUrl',
|
|
1930
|
+
};
|
|
1931
|
+
});
|
|
1932
|
+
|
|
1933
|
+
it('applyLocusDeltaData gets full locus on DESYNC action if we do not have a syncUrl and destroys the meeting if that fails', () => {
|
|
1934
|
+
meeting.meetingRequest.getLocusDTO.rejects(new Error('fake error'));
|
|
1935
|
+
|
|
1936
|
+
locusInfo.locusParser.workingCopy = {}; // no syncUrl
|
|
1937
|
+
|
|
1938
|
+
// Since we have a promise inside a function we want to test that's not returned,
|
|
1939
|
+
// we will wait and stub it's last function to resolve this waiting promise.
|
|
1940
|
+
return new Promise((resolve) => {
|
|
1941
|
+
webex.meetings.destroy.callsFake(() => resolve());
|
|
1942
|
+
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
|
1943
|
+
}).then(() => {
|
|
1944
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'fullSyncUrl'});
|
|
1945
|
+
|
|
1946
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
|
1947
|
+
assert.notCalled(meeting.locusInfo.onFullLocus);
|
|
1948
|
+
assert.notCalled(locusInfo.locusParser.resume);
|
|
1949
|
+
|
|
1950
|
+
assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
|
|
1951
|
+
});
|
|
1952
|
+
});
|
|
1953
|
+
|
|
1954
|
+
it('applyLocusDeltaData first tries a delta sync on DESYNC action and if that fails, does a full locus sync', () => {
|
|
1955
|
+
meeting.meetingRequest.getLocusDTO.onCall(0).rejects(new Error('fake error'));
|
|
1956
|
+
meeting.meetingRequest.getLocusDTO.onCall(1).resolves({body: fakeFullLocusDto});
|
|
1957
|
+
|
|
1958
|
+
// Since we have a promise inside a function we want to test that's not returned,
|
|
1959
|
+
// we will wait and stub it's last function to resolve this waiting promise.
|
|
1960
|
+
return new Promise((resolve) => {
|
|
1961
|
+
locusInfo.locusParser.resume.callsFake(() => resolve());
|
|
1962
|
+
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
|
1963
|
+
}).then(() => {
|
|
1964
|
+
assert.calledTwice(meeting.meetingRequest.getLocusDTO);
|
|
1965
|
+
|
|
1966
|
+
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [{url: 'deltaSyncUrl'}]);
|
|
1967
|
+
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [{url: 'fullSyncUrl'}]);
|
|
1968
|
+
|
|
1969
|
+
assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
|
|
1970
|
+
correlationId: meeting.correlationId,
|
|
1971
|
+
url: 'deltaSyncUrl',
|
|
1972
|
+
reason: 'fake error',
|
|
1973
|
+
errorName: 'Error',
|
|
1974
|
+
stack: sinon.match.any,
|
|
1975
|
+
code: sinon.match.any,
|
|
1976
|
+
});
|
|
1977
|
+
|
|
1978
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
|
1979
|
+
assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
|
|
1980
|
+
assert.calledOnce(locusInfo.locusParser.resume);
|
|
1981
|
+
});
|
|
1982
|
+
});
|
|
1983
|
+
|
|
1984
|
+
it('applyLocusDeltaData destroys the meeting if both delta sync and full sync fail', () => {
|
|
1985
|
+
meeting.meetingRequest.getLocusDTO.rejects(new Error('fake error'));
|
|
1986
|
+
|
|
1987
|
+
// Since we have a promise inside a function we want to test that's not returned,
|
|
1988
|
+
// we will wait and stub it's last function to resolve this waiting promise.
|
|
1989
|
+
return new Promise((resolve) => {
|
|
1990
|
+
webex.meetings.destroy.callsFake(() => resolve());
|
|
1991
|
+
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
|
1992
|
+
}).then(() => {
|
|
1993
|
+
assert.calledTwice(meeting.meetingRequest.getLocusDTO);
|
|
1994
|
+
|
|
1995
|
+
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [{url: 'deltaSyncUrl'}]);
|
|
1996
|
+
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [{url: 'fullSyncUrl'}]);
|
|
1997
|
+
|
|
1998
|
+
assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
|
|
1999
|
+
correlationId: meeting.correlationId,
|
|
2000
|
+
url: 'deltaSyncUrl',
|
|
2001
|
+
reason: 'fake error',
|
|
2002
|
+
errorName: 'Error',
|
|
2003
|
+
stack: sinon.match.any,
|
|
2004
|
+
code: sinon.match.any,
|
|
2005
|
+
});
|
|
2006
|
+
|
|
2007
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
|
2008
|
+
assert.notCalled(meeting.locusInfo.onFullLocus);
|
|
2009
|
+
assert.notCalled(locusInfo.locusParser.resume);
|
|
2010
|
+
|
|
2011
|
+
assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
|
|
2012
|
+
});
|
|
2013
|
+
});
|
|
2014
|
+
});
|
|
2015
|
+
|
|
2016
|
+
it('onDeltaLocus handle delta data', () => {
|
|
2017
|
+
fakeLocus.participants = {};
|
|
2018
|
+
const fakeBreakout = {
|
|
2019
|
+
sessionId: 'sessionId',
|
|
2020
|
+
groupId: 'groupId',
|
|
2021
|
+
};
|
|
2022
|
+
|
|
2023
|
+
fakeLocus.controls = {
|
|
2024
|
+
breakout: fakeBreakout,
|
|
2025
|
+
};
|
|
2026
|
+
locusInfo.controls = {
|
|
2027
|
+
breakout: {
|
|
2028
|
+
sessionId: 'sessionId',
|
|
2029
|
+
groupId: 'groupId',
|
|
2030
|
+
},
|
|
2031
|
+
};
|
|
2032
|
+
locusInfo.updateParticipants = sinon.stub();
|
|
2033
|
+
locusInfo.onDeltaLocus(fakeLocus);
|
|
2034
|
+
assert.calledWith(locusInfo.updateParticipants, {}, false);
|
|
2035
|
+
|
|
2036
|
+
fakeLocus.controls.breakout.sessionId = 'sessionId2';
|
|
2037
|
+
locusInfo.onDeltaLocus(fakeLocus);
|
|
2038
|
+
assert.calledWith(locusInfo.updateParticipants, {}, true);
|
|
2039
|
+
});
|
|
2040
|
+
});
|
|
2041
|
+
|
|
2042
|
+
describe('#updateLocusCache', () => {
|
|
2043
|
+
it('cache it if income locus is main session locus', () => {
|
|
2044
|
+
const locus = {url: 'url'};
|
|
2045
|
+
locusInfo.mainSessionLocusCache = null;
|
|
2046
|
+
locusInfo.updateLocusCache(locus);
|
|
2047
|
+
|
|
2048
|
+
assert.deepEqual(locusInfo.mainSessionLocusCache, locus);
|
|
2049
|
+
});
|
|
2050
|
+
|
|
2051
|
+
it('not cache it if income locus is breakout session locus', () => {
|
|
2052
|
+
const locus = {url: 'url', controls: {breakout: {sessionType: 'BREAKOUT'}}};
|
|
2053
|
+
locusInfo.mainSessionLocusCache = null;
|
|
2054
|
+
locusInfo.updateLocusCache(locus);
|
|
2055
|
+
|
|
2056
|
+
assert.isNull(locusInfo.mainSessionLocusCache);
|
|
2057
|
+
});
|
|
2058
|
+
});
|
|
2059
|
+
|
|
2060
|
+
describe('#getTheLocusToUpdate', () => {
|
|
2061
|
+
it('return the cache locus if return to main session', () => {
|
|
2062
|
+
locusInfo.mainSessionLocusCache = {url: 'url'};
|
|
2063
|
+
locusInfo.controls = {
|
|
2064
|
+
breakout: {
|
|
2065
|
+
sessionType: 'BREAKOUT',
|
|
2066
|
+
},
|
|
2067
|
+
};
|
|
2068
|
+
const newLocus = {
|
|
2069
|
+
controls: {
|
|
2070
|
+
breakout: {
|
|
2071
|
+
sessionType: 'MAIN',
|
|
2072
|
+
},
|
|
2073
|
+
},
|
|
2074
|
+
};
|
|
2075
|
+
|
|
2076
|
+
assert.deepEqual(locusInfo.getTheLocusToUpdate(newLocus), {url: 'url'});
|
|
2077
|
+
});
|
|
2078
|
+
|
|
2079
|
+
it('return the new locus if return to main session but no cache', () => {
|
|
2080
|
+
locusInfo.mainSessionLocusCache = null;
|
|
2081
|
+
locusInfo.controls = {
|
|
2082
|
+
breakout: {
|
|
2083
|
+
sessionType: 'BREAKOUT',
|
|
2084
|
+
},
|
|
2085
|
+
};
|
|
2086
|
+
const newLocus = {
|
|
2087
|
+
controls: {
|
|
2088
|
+
breakout: {
|
|
2089
|
+
sessionType: 'MAIN',
|
|
2090
|
+
},
|
|
2091
|
+
},
|
|
2092
|
+
};
|
|
2093
|
+
|
|
2094
|
+
assert.deepEqual(locusInfo.getTheLocusToUpdate(newLocus), newLocus);
|
|
2095
|
+
});
|
|
2096
|
+
|
|
2097
|
+
it('return the new locus if not return to main session', () => {
|
|
2098
|
+
locusInfo.mainSessionLocusCache = {url: 'url'};
|
|
2099
|
+
locusInfo.controls = {
|
|
2100
|
+
breakout: {
|
|
2101
|
+
sessionType: 'MAIN',
|
|
2102
|
+
},
|
|
2103
|
+
};
|
|
2104
|
+
const newLocus = {
|
|
2105
|
+
controls: {
|
|
2106
|
+
breakout: {
|
|
2107
|
+
sessionType: 'BREAKOUT',
|
|
2108
|
+
},
|
|
2109
|
+
},
|
|
2110
|
+
};
|
|
2111
|
+
|
|
2112
|
+
assert.deepEqual(locusInfo.getTheLocusToUpdate(newLocus), newLocus);
|
|
2113
|
+
});
|
|
2114
|
+
});
|
|
2115
|
+
|
|
2116
|
+
describe('#mergeParticipants', () => {
|
|
2117
|
+
let participants;
|
|
2118
|
+
let sourceParticipants;
|
|
2119
|
+
beforeEach(() => {
|
|
2120
|
+
participants = [{id: '111', status: 'JOINED'}, {id: '222'}];
|
|
2121
|
+
sourceParticipants = [{id: '111', status: 'LEFT'}, {id: '333'}];
|
|
2122
|
+
});
|
|
2123
|
+
|
|
2124
|
+
it('merge the participants, replace it by id if exist in old array', () => {
|
|
2125
|
+
const result = locusInfo.mergeParticipants(participants, sourceParticipants);
|
|
2126
|
+
assert.deepEqual(result, [{id: '111', status: 'LEFT'}, {id: '222'}, {id: '333'}]);
|
|
2127
|
+
});
|
|
2128
|
+
|
|
2129
|
+
it('return new participants if previous participants is empty', () => {
|
|
2130
|
+
const result = locusInfo.mergeParticipants([], sourceParticipants);
|
|
2131
|
+
assert.deepEqual(result, sourceParticipants);
|
|
2132
|
+
});
|
|
2133
|
+
|
|
2134
|
+
it('return new participants if previous participants is null/undefined', () => {
|
|
2135
|
+
let result = locusInfo.mergeParticipants(null, sourceParticipants);
|
|
2136
|
+
assert.deepEqual(result, sourceParticipants);
|
|
2137
|
+
|
|
2138
|
+
result = locusInfo.mergeParticipants(undefined, sourceParticipants);
|
|
2139
|
+
assert.deepEqual(result, sourceParticipants);
|
|
2140
|
+
});
|
|
2141
|
+
|
|
2142
|
+
it('return previous participants if new participants is empty', () => {
|
|
2143
|
+
const result = locusInfo.mergeParticipants(participants, []);
|
|
2144
|
+
assert.deepEqual(result, participants);
|
|
2145
|
+
});
|
|
2146
|
+
|
|
2147
|
+
it('return previous participants if new participants is null/undefined', () => {
|
|
2148
|
+
let result = locusInfo.mergeParticipants(participants, null);
|
|
2149
|
+
assert.deepEqual(result, participants);
|
|
2150
|
+
|
|
2151
|
+
result = locusInfo.mergeParticipants(participants, undefined);
|
|
2152
|
+
assert.deepEqual(result, participants);
|
|
2153
|
+
});
|
|
2154
|
+
});
|
|
2155
|
+
|
|
2156
|
+
describe('#updateMainSessionLocusCache', () => {
|
|
2157
|
+
let cachedLocus;
|
|
2158
|
+
let newLocus;
|
|
2159
|
+
beforeEach(() => {
|
|
2160
|
+
cachedLocus = {
|
|
2161
|
+
controls: {},
|
|
2162
|
+
participants: [],
|
|
2163
|
+
info: {webExMeetingId: 'testId1', topic: 'test'},
|
|
2164
|
+
};
|
|
2165
|
+
newLocus = {
|
|
2166
|
+
self: {},
|
|
2167
|
+
participants: [{id: '111'}],
|
|
2168
|
+
info: {testId: 'testId2', webExMeetingName: 'hello'},
|
|
2169
|
+
};
|
|
2170
|
+
});
|
|
2171
|
+
it('shallow merge new locus into cache', () => {
|
|
2172
|
+
locusInfo.mainSessionLocusCache = cachedLocus;
|
|
2173
|
+
locusInfo.updateMainSessionLocusCache(newLocus);
|
|
2174
|
+
|
|
2175
|
+
assert.deepEqual(locusInfo.mainSessionLocusCache, {
|
|
2176
|
+
controls: {},
|
|
2177
|
+
participants: [{id: '111'}],
|
|
2178
|
+
info: {testId: 'testId2', webExMeetingName: 'hello'},
|
|
2179
|
+
self: {},
|
|
2180
|
+
});
|
|
2181
|
+
});
|
|
2182
|
+
|
|
2183
|
+
it('cache new locus if no cache before', () => {
|
|
2184
|
+
locusInfo.mainSessionLocusCache = null;
|
|
2185
|
+
locusInfo.updateMainSessionLocusCache(newLocus);
|
|
2186
|
+
|
|
2187
|
+
assert.deepEqual(locusInfo.mainSessionLocusCache, newLocus);
|
|
2188
|
+
});
|
|
2189
|
+
|
|
2190
|
+
it('do nothing if new locus is null', () => {
|
|
2191
|
+
locusInfo.mainSessionLocusCache = cachedLocus;
|
|
2192
|
+
locusInfo.updateMainSessionLocusCache(null);
|
|
2193
|
+
|
|
2194
|
+
assert.deepEqual(locusInfo.mainSessionLocusCache, cachedLocus);
|
|
2195
|
+
});
|
|
2196
|
+
});
|
|
2197
|
+
|
|
2198
|
+
describe('#clearMainSessionLocusCache', () => {
|
|
2199
|
+
it('clear main session locus cache', () => {
|
|
2200
|
+
locusInfo.mainSessionLocusCache = {controls: {}};
|
|
2201
|
+
locusInfo.clearMainSessionLocusCache();
|
|
2202
|
+
assert.isNull(locusInfo.mainSessionLocusCache);
|
|
2203
|
+
});
|
|
1332
2204
|
});
|
|
1333
2205
|
|
|
1334
2206
|
describe('#handleOneonOneEvent', () => {
|
|
@@ -1371,5 +2243,404 @@ describe('plugin-meetings', () => {
|
|
|
1371
2243
|
);
|
|
1372
2244
|
});
|
|
1373
2245
|
});
|
|
2246
|
+
|
|
2247
|
+
describe('#isMeetingActive', () => {
|
|
2248
|
+
it('sends client event correctly for state = inactive', () => {
|
|
2249
|
+
locusInfo.parsedLocus = {
|
|
2250
|
+
fullState: {
|
|
2251
|
+
type: _CALL_,
|
|
2252
|
+
},
|
|
2253
|
+
};
|
|
2254
|
+
|
|
2255
|
+
locusInfo.fullState = {
|
|
2256
|
+
state: LOCUS.STATE.INACTIVE,
|
|
2257
|
+
};
|
|
2258
|
+
|
|
2259
|
+
locusInfo.isMeetingActive();
|
|
2260
|
+
|
|
2261
|
+
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
|
2262
|
+
name: 'client.call.remote-ended',
|
|
2263
|
+
options: {
|
|
2264
|
+
meetingId: locusInfo.meetingId,
|
|
2265
|
+
},
|
|
2266
|
+
});
|
|
2267
|
+
});
|
|
2268
|
+
|
|
2269
|
+
it('sends client event correctly for state = PARTNER_LEFT', () => {
|
|
2270
|
+
locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
|
|
2271
|
+
locusInfo.parsedLocus = {
|
|
2272
|
+
fullState: {
|
|
2273
|
+
type: _CALL_,
|
|
2274
|
+
},
|
|
2275
|
+
self: {
|
|
2276
|
+
state: MEETING_STATE.STATES.DECLINED,
|
|
2277
|
+
},
|
|
2278
|
+
};
|
|
2279
|
+
locusInfo.isMeetingActive();
|
|
2280
|
+
|
|
2281
|
+
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
|
2282
|
+
name: 'client.call.remote-ended',
|
|
2283
|
+
options: {
|
|
2284
|
+
meetingId: locusInfo.meetingId,
|
|
2285
|
+
},
|
|
2286
|
+
});
|
|
2287
|
+
});
|
|
2288
|
+
|
|
2289
|
+
it('sends client event correctly for state = SELF_LEFT', () => {
|
|
2290
|
+
locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
|
|
2291
|
+
locusInfo.parsedLocus = {
|
|
2292
|
+
fullState: {
|
|
2293
|
+
type: _CALL_,
|
|
2294
|
+
},
|
|
2295
|
+
self: {
|
|
2296
|
+
state: MEETING_STATE.STATES.LEFT,
|
|
2297
|
+
},
|
|
2298
|
+
};
|
|
2299
|
+
|
|
2300
|
+
locusInfo.isMeetingActive();
|
|
2301
|
+
|
|
2302
|
+
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
|
2303
|
+
name: 'client.call.remote-ended',
|
|
2304
|
+
options: {
|
|
2305
|
+
meetingId: locusInfo.meetingId,
|
|
2306
|
+
},
|
|
2307
|
+
});
|
|
2308
|
+
});
|
|
2309
|
+
|
|
2310
|
+
it('sends client event correctly for state = MEETING_INACTIVE_TERMINATING', () => {
|
|
2311
|
+
locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
|
|
2312
|
+
locusInfo.parsedLocus = {
|
|
2313
|
+
fullState: {
|
|
2314
|
+
type: _MEETING_,
|
|
2315
|
+
},
|
|
2316
|
+
};
|
|
2317
|
+
|
|
2318
|
+
locusInfo.fullState = {
|
|
2319
|
+
state: LOCUS.STATE.INACTIVE,
|
|
2320
|
+
};
|
|
2321
|
+
|
|
2322
|
+
locusInfo.isMeetingActive();
|
|
2323
|
+
|
|
2324
|
+
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
|
2325
|
+
name: 'client.call.remote-ended',
|
|
2326
|
+
options: {
|
|
2327
|
+
meetingId: locusInfo.meetingId,
|
|
2328
|
+
},
|
|
2329
|
+
});
|
|
2330
|
+
});
|
|
2331
|
+
|
|
2332
|
+
it('sends client event correctly for state = FULLSTATE_REMOVED', () => {
|
|
2333
|
+
locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
|
|
2334
|
+
locusInfo.parsedLocus = {
|
|
2335
|
+
fullState: {
|
|
2336
|
+
type: _MEETING_,
|
|
2337
|
+
},
|
|
2338
|
+
};
|
|
2339
|
+
|
|
2340
|
+
locusInfo.fullState = {
|
|
2341
|
+
removed: true,
|
|
2342
|
+
};
|
|
2343
|
+
|
|
2344
|
+
locusInfo.isMeetingActive();
|
|
2345
|
+
|
|
2346
|
+
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
|
2347
|
+
name: 'client.call.remote-ended',
|
|
2348
|
+
options: {
|
|
2349
|
+
meetingId: locusInfo.meetingId,
|
|
2350
|
+
},
|
|
2351
|
+
});
|
|
2352
|
+
});
|
|
2353
|
+
});
|
|
2354
|
+
|
|
2355
|
+
// semi-integration tests that use real LocusInfo with real Parser
|
|
2356
|
+
// and test various scenarios related to handling out-of-order Locus delta events
|
|
2357
|
+
describe('handling of out-of-order Locus delta events', () => {
|
|
2358
|
+
let clock;
|
|
2359
|
+
|
|
2360
|
+
const generateDeltaEvent = (base, sequence) => {
|
|
2361
|
+
return {
|
|
2362
|
+
baseSequence: {
|
|
2363
|
+
rangeStart: 0,
|
|
2364
|
+
rangeEnd: 0,
|
|
2365
|
+
entries: [base],
|
|
2366
|
+
},
|
|
2367
|
+
sequence: {
|
|
2368
|
+
rangeStart: 0,
|
|
2369
|
+
rangeEnd: 0,
|
|
2370
|
+
entries: [sequence],
|
|
2371
|
+
},
|
|
2372
|
+
syncUrl: `fake sync url for sequence ${sequence}`,
|
|
2373
|
+
self: {
|
|
2374
|
+
person: {
|
|
2375
|
+
id: 'test person id',
|
|
2376
|
+
},
|
|
2377
|
+
},
|
|
2378
|
+
};
|
|
2379
|
+
};
|
|
2380
|
+
|
|
2381
|
+
// a list of example delta events, sorted by time and each event is based on the previous one
|
|
2382
|
+
const deltaEvents = [
|
|
2383
|
+
generateDeltaEvent(10, 20), // 0
|
|
2384
|
+
generateDeltaEvent(20, 30), // 1
|
|
2385
|
+
generateDeltaEvent(30, 40), // 2
|
|
2386
|
+
generateDeltaEvent(40, 50), // 3
|
|
2387
|
+
generateDeltaEvent(50, 60), // 4
|
|
2388
|
+
generateDeltaEvent(60, 70), // 5
|
|
2389
|
+
generateDeltaEvent(70, 80), // 6
|
|
2390
|
+
generateDeltaEvent(80, 90), // 7
|
|
2391
|
+
generateDeltaEvent(90, 100), // 8
|
|
2392
|
+
];
|
|
2393
|
+
|
|
2394
|
+
let updateLocusInfoStub; // we use this stub to verify that an event has been fully processed
|
|
2395
|
+
let syncRequestStub;
|
|
2396
|
+
|
|
2397
|
+
beforeEach(() => {
|
|
2398
|
+
clock = sinon.useFakeTimers();
|
|
2399
|
+
|
|
2400
|
+
sinon.stub(locusInfo, 'updateParticipantDeltas');
|
|
2401
|
+
sinon.stub(locusInfo, 'updateParticipants');
|
|
2402
|
+
sinon.stub(locusInfo, 'isMeetingActive'),
|
|
2403
|
+
sinon.stub(locusInfo, 'handleOneOnOneEvent'),
|
|
2404
|
+
(updateLocusInfoStub = sinon.stub(locusInfo, 'updateLocusInfo'));
|
|
2405
|
+
syncRequestStub = sinon.stub().resolves({body: {}});
|
|
2406
|
+
|
|
2407
|
+
mockMeeting.locusInfo = locusInfo;
|
|
2408
|
+
mockMeeting.locusUrl = 'fake locus url';
|
|
2409
|
+
mockMeeting.meetingRequest = {
|
|
2410
|
+
getLocusDTO: syncRequestStub,
|
|
2411
|
+
};
|
|
2412
|
+
|
|
2413
|
+
locusInfo.onFullLocus({
|
|
2414
|
+
sequence: {
|
|
2415
|
+
rangeStart: 0,
|
|
2416
|
+
rangeEnd: 0,
|
|
2417
|
+
entries: [10],
|
|
2418
|
+
},
|
|
2419
|
+
self: {
|
|
2420
|
+
person: {
|
|
2421
|
+
id: 'test person id',
|
|
2422
|
+
},
|
|
2423
|
+
},
|
|
2424
|
+
});
|
|
2425
|
+
|
|
2426
|
+
updateLocusInfoStub.resetHistory();
|
|
2427
|
+
});
|
|
2428
|
+
|
|
2429
|
+
afterEach(() => {
|
|
2430
|
+
clock.restore();
|
|
2431
|
+
});
|
|
2432
|
+
|
|
2433
|
+
it('queues out-of-order deltas until it receives a correct delta', () => {
|
|
2434
|
+
// send some out-of-order deltas
|
|
2435
|
+
locusInfo.handleLocusDelta(deltaEvents[1], mockMeeting);
|
|
2436
|
+
locusInfo.handleLocusDelta(deltaEvents[4], mockMeeting);
|
|
2437
|
+
|
|
2438
|
+
// they should be queued and not processed
|
|
2439
|
+
assert.notCalled(updateLocusInfoStub);
|
|
2440
|
+
|
|
2441
|
+
// now one of the missing ones, but not the one SDK is really waiting for
|
|
2442
|
+
locusInfo.handleLocusDelta(deltaEvents[2], mockMeeting);
|
|
2443
|
+
|
|
2444
|
+
// still nothing should be processed
|
|
2445
|
+
assert.notCalled(updateLocusInfoStub);
|
|
2446
|
+
|
|
2447
|
+
// now send the one SDK is waiting for
|
|
2448
|
+
locusInfo.handleLocusDelta(deltaEvents[0], mockMeeting);
|
|
2449
|
+
|
|
2450
|
+
// so deltaEvents with indexes 1,2,3 can be processed, but 5 still not, because 4 is missing
|
|
2451
|
+
assert.callCount(updateLocusInfoStub, 3);
|
|
2452
|
+
assert.calledWith(updateLocusInfoStub.getCall(0), deltaEvents[0]);
|
|
2453
|
+
assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[1]);
|
|
2454
|
+
assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[2]);
|
|
2455
|
+
|
|
2456
|
+
updateLocusInfoStub.resetHistory();
|
|
2457
|
+
|
|
2458
|
+
// now send deltaEvents[4]
|
|
2459
|
+
locusInfo.handleLocusDelta(deltaEvents[3], mockMeeting);
|
|
2460
|
+
|
|
2461
|
+
// and verify deltaEvents[4] and deltaEvents[5] have been processed
|
|
2462
|
+
assert.callCount(updateLocusInfoStub, 2);
|
|
2463
|
+
assert.calledWith(updateLocusInfoStub.getCall(0), deltaEvents[3]);
|
|
2464
|
+
assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[4]);
|
|
2465
|
+
});
|
|
2466
|
+
|
|
2467
|
+
it('handles out-of-order deltas correctly even if all arrive in reverse order', () => {
|
|
2468
|
+
// send a bunch deltas in reverse order
|
|
2469
|
+
for (let i = 4; i >= 0; i--) {
|
|
2470
|
+
locusInfo.handleLocusDelta(deltaEvents[i], mockMeeting);
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
// they should be queued and then processed in correct order
|
|
2474
|
+
assert.callCount(updateLocusInfoStub, 5);
|
|
2475
|
+
assert.calledWith(updateLocusInfoStub.getCall(0), deltaEvents[0]);
|
|
2476
|
+
assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[1]);
|
|
2477
|
+
assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[2]);
|
|
2478
|
+
assert.calledWith(updateLocusInfoStub.getCall(3), deltaEvents[3]);
|
|
2479
|
+
assert.calledWith(updateLocusInfoStub.getCall(4), deltaEvents[4]);
|
|
2480
|
+
});
|
|
2481
|
+
|
|
2482
|
+
it('sends a sync request using syncUrl if it receives at least 1 delta event and processes later deltas after sync correctly', async () => {
|
|
2483
|
+
// the test first sends an initial "good" delta
|
|
2484
|
+
const initialDeltaIdx = 0;
|
|
2485
|
+
const initialDelta = deltaEvents[initialDeltaIdx];
|
|
2486
|
+
|
|
2487
|
+
// then it sends a bunch of out-of-order deltas (at least 6 to trigger a sync), last one being lastOooDelta
|
|
2488
|
+
const firstOooDeltaIdx = 2;
|
|
2489
|
+
const lastOooDeltaIdx = 7;
|
|
2490
|
+
const lastOooDelta = deltaEvents[lastOooDeltaIdx];
|
|
2491
|
+
|
|
2492
|
+
// and finally, after the sync it sends another "good" delta
|
|
2493
|
+
const goodDeltaAfterSync = deltaEvents[8];
|
|
2494
|
+
|
|
2495
|
+
const deltaLocusFromSyncResponse = {
|
|
2496
|
+
baseSequence: {
|
|
2497
|
+
rangeStart: 0,
|
|
2498
|
+
rangeEnd: 0,
|
|
2499
|
+
entries: [initialDelta.sequence.entries[0]],
|
|
2500
|
+
},
|
|
2501
|
+
sequence: {
|
|
2502
|
+
rangeStart: 0,
|
|
2503
|
+
rangeEnd: 0,
|
|
2504
|
+
entries: [lastOooDelta.sequence.entries[0]],
|
|
2505
|
+
},
|
|
2506
|
+
syncUrl: `fake sync url for sequence ${lastOooDelta.sequence.entries[0]}`,
|
|
2507
|
+
self: {
|
|
2508
|
+
person: {
|
|
2509
|
+
id: 'test person id',
|
|
2510
|
+
},
|
|
2511
|
+
},
|
|
2512
|
+
};
|
|
2513
|
+
|
|
2514
|
+
syncRequestStub.resolves({
|
|
2515
|
+
body: deltaLocusFromSyncResponse,
|
|
2516
|
+
});
|
|
2517
|
+
|
|
2518
|
+
// send one correct delta so that SDK has the syncUrl
|
|
2519
|
+
locusInfo.handleLocusDelta(initialDelta, mockMeeting);
|
|
2520
|
+
|
|
2521
|
+
updateLocusInfoStub.resetHistory();
|
|
2522
|
+
|
|
2523
|
+
// send 6 out-of-order deltas to trigger a sync (we're skipping deltaEvents[1])
|
|
2524
|
+
for (let i = firstOooDeltaIdx; i <= lastOooDeltaIdx; i++) {
|
|
2525
|
+
locusInfo.handleLocusDelta(deltaEvents[i], mockMeeting);
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2528
|
+
await testUtils.flushPromises();
|
|
2529
|
+
|
|
2530
|
+
// check that sync was done using the correct syncUrl
|
|
2531
|
+
assert.calledOnceWithExactly(syncRequestStub, {url: initialDelta.syncUrl});
|
|
2532
|
+
assert.calledOnceWithExactly(updateLocusInfoStub, deltaLocusFromSyncResponse);
|
|
2533
|
+
|
|
2534
|
+
updateLocusInfoStub.resetHistory();
|
|
2535
|
+
|
|
2536
|
+
// now send another delta - a good one, it should be processed as normal
|
|
2537
|
+
locusInfo.handleLocusDelta(goodDeltaAfterSync, mockMeeting);
|
|
2538
|
+
|
|
2539
|
+
assert.calledOnceWithExactly(updateLocusInfoStub, goodDeltaAfterSync);
|
|
2540
|
+
});
|
|
2541
|
+
|
|
2542
|
+
it('does a sync if blocked on out-of-order deltas for too long', async () => {
|
|
2543
|
+
// stub random so that the timer fires after 12500 ms
|
|
2544
|
+
sinon.stub(Math, 'random').returns(0.5);
|
|
2545
|
+
|
|
2546
|
+
const oooDelta = deltaEvents[3];
|
|
2547
|
+
|
|
2548
|
+
// setup the stubs so that the sync request receives a full DTO with the sequence equal to the out-of-order delta we simulate
|
|
2549
|
+
const fullLocus = {
|
|
2550
|
+
sequence: oooDelta.sequence,
|
|
2551
|
+
};
|
|
2552
|
+
syncRequestStub.resolves({
|
|
2553
|
+
body: fullLocus,
|
|
2554
|
+
});
|
|
2555
|
+
|
|
2556
|
+
// send an out-of-order delta
|
|
2557
|
+
locusInfo.handleLocusDelta(oooDelta, mockMeeting);
|
|
2558
|
+
|
|
2559
|
+
assert.calledOnceWithExactly(sendBehavioralMetricStub, 'js_sdk_locus_delta_ooo', { stack: sinon.match.any})
|
|
2560
|
+
|
|
2561
|
+
await clock.tickAsync(12499);
|
|
2562
|
+
await testUtils.flushPromises();
|
|
2563
|
+
assert.notCalled(syncRequestStub);
|
|
2564
|
+
assert.notCalled(updateLocusInfoStub);
|
|
2565
|
+
|
|
2566
|
+
await clock.tickAsync(1);
|
|
2567
|
+
await testUtils.flushPromises();
|
|
2568
|
+
|
|
2569
|
+
assert.calledOnceWithExactly(syncRequestStub, {url: mockMeeting.locusUrl});
|
|
2570
|
+
assert.calledOnceWithExactly(updateLocusInfoStub, fullLocus);
|
|
2571
|
+
});
|
|
2572
|
+
|
|
2573
|
+
it('does a sync if out-of-order deltas queue becomes too big', async () => {
|
|
2574
|
+
// setup the stubs so that the sync request receives a full DTO with the sequence equal to the out-of-order delta we simulate
|
|
2575
|
+
const fullLocus = {
|
|
2576
|
+
sequence: deltaEvents[6].sequence,
|
|
2577
|
+
};
|
|
2578
|
+
syncRequestStub.resolves({
|
|
2579
|
+
body: fullLocus,
|
|
2580
|
+
});
|
|
2581
|
+
|
|
2582
|
+
// send 5 deltas, starting from deltaEvents[1] so that SDK is blocked waiting for deltaEvents[0]
|
|
2583
|
+
for (let i = 0; i < 5; i++) {
|
|
2584
|
+
locusInfo.handleLocusDelta(deltaEvents[i + 1], mockMeeting);
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2587
|
+
// nothing should happen, SDK should still be waiting for deltaEvents[0]
|
|
2588
|
+
assert.notCalled(syncRequestStub);
|
|
2589
|
+
assert.notCalled(updateLocusInfoStub);
|
|
2590
|
+
|
|
2591
|
+
// now send one more out-of-order delta to trigger a sync request
|
|
2592
|
+
locusInfo.handleLocusDelta(deltaEvents[6], mockMeeting);
|
|
2593
|
+
|
|
2594
|
+
await testUtils.flushPromises();
|
|
2595
|
+
|
|
2596
|
+
// check sync was done
|
|
2597
|
+
assert.calledOnceWithExactly(syncRequestStub, {url: mockMeeting.locusUrl});
|
|
2598
|
+
assert.calledOnceWithExactly(updateLocusInfoStub, fullLocus);
|
|
2599
|
+
});
|
|
2600
|
+
|
|
2601
|
+
it('processes delta events that are not included in sync response', async () => {
|
|
2602
|
+
// this test sends a bunch of out-of-order deltas, this triggers a sync
|
|
2603
|
+
// but the full locus response doesn't include the last 2 deltas received, so
|
|
2604
|
+
// we check that these 2 deltas are also processed after sync response
|
|
2605
|
+
const fullLocusFromSyncResponse = {
|
|
2606
|
+
baseSequence: {
|
|
2607
|
+
rangeStart: 0,
|
|
2608
|
+
rangeEnd: 0,
|
|
2609
|
+
entries: [deltaEvents[0].sequence.entries[0]],
|
|
2610
|
+
},
|
|
2611
|
+
sequence: {
|
|
2612
|
+
rangeStart: 0,
|
|
2613
|
+
rangeEnd: 0,
|
|
2614
|
+
entries: [deltaEvents[5].sequence.entries[0]],
|
|
2615
|
+
},
|
|
2616
|
+
syncUrl: `fake sync url for sequence ${deltaEvents[5].sequence.entries[0]}`,
|
|
2617
|
+
self: {
|
|
2618
|
+
person: {
|
|
2619
|
+
id: 'test person id',
|
|
2620
|
+
},
|
|
2621
|
+
},
|
|
2622
|
+
};
|
|
2623
|
+
|
|
2624
|
+
syncRequestStub.resolves({
|
|
2625
|
+
body: fullLocusFromSyncResponse,
|
|
2626
|
+
});
|
|
2627
|
+
|
|
2628
|
+
// send at least 6 out-of-order deltas to trigger a sync (we're skipping deltaEvents[0])
|
|
2629
|
+
for (let i = 1; i <= 7; i++) {
|
|
2630
|
+
locusInfo.handleLocusDelta(deltaEvents[i], mockMeeting);
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
await testUtils.flushPromises();
|
|
2634
|
+
|
|
2635
|
+
// check that sync was done
|
|
2636
|
+
assert.calledOnceWithExactly(syncRequestStub, {url: mockMeeting.locusUrl});
|
|
2637
|
+
|
|
2638
|
+
// and that remaining deltas from the queue that were not included in full Locus were also processed
|
|
2639
|
+
assert.callCount(updateLocusInfoStub, 3);
|
|
2640
|
+
assert.calledWith(updateLocusInfoStub.getCall(0), fullLocusFromSyncResponse);
|
|
2641
|
+
assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[6]);
|
|
2642
|
+
assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[7]);
|
|
2643
|
+
});
|
|
2644
|
+
});
|
|
1374
2645
|
});
|
|
1375
2646
|
});
|