@webex/plugin-meetings 3.9.0-webinar5k.1 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/constants.js +24 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/controls-options-manager/index.js +22 -5
  6. package/dist/controls-options-manager/index.js.map +1 -1
  7. package/dist/index.js +2 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/interceptors/index.js +7 -0
  10. package/dist/interceptors/index.js.map +1 -1
  11. package/dist/interceptors/locusRouteToken.js +116 -0
  12. package/dist/interceptors/locusRouteToken.js.map +1 -0
  13. package/dist/interpretation/index.js +1 -1
  14. package/dist/interpretation/siLanguage.js +1 -1
  15. package/dist/locus-info/controlsUtils.js +11 -2
  16. package/dist/locus-info/controlsUtils.js.map +1 -1
  17. package/dist/locus-info/index.js +76 -322
  18. package/dist/locus-info/index.js.map +1 -1
  19. package/dist/locus-info/parser.js +4 -1
  20. package/dist/locus-info/parser.js.map +1 -1
  21. package/dist/media/properties.js +53 -5
  22. package/dist/media/properties.js.map +1 -1
  23. package/dist/meeting/in-meeting-actions.js +14 -0
  24. package/dist/meeting/in-meeting-actions.js.map +1 -1
  25. package/dist/meeting/index.js +467 -277
  26. package/dist/meeting/index.js.map +1 -1
  27. package/dist/meeting/request.js +177 -14
  28. package/dist/meeting/request.js.map +1 -1
  29. package/dist/meeting/type.js +7 -0
  30. package/dist/meeting/type.js.map +1 -0
  31. package/dist/meeting/util.js +100 -3
  32. package/dist/meeting/util.js.map +1 -1
  33. package/dist/meeting-info/meeting-info-v2.js +29 -21
  34. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  35. package/dist/meetings/index.js +20 -16
  36. package/dist/meetings/index.js.map +1 -1
  37. package/dist/member/index.js +9 -0
  38. package/dist/member/index.js.map +1 -1
  39. package/dist/member/util.js +10 -0
  40. package/dist/member/util.js.map +1 -1
  41. package/dist/members/index.js +10 -7
  42. package/dist/members/index.js.map +1 -1
  43. package/dist/members/util.js +7 -2
  44. package/dist/members/util.js.map +1 -1
  45. package/dist/metrics/constants.js +2 -1
  46. package/dist/metrics/constants.js.map +1 -1
  47. package/dist/multistream/mediaRequestManager.js +1 -1
  48. package/dist/multistream/mediaRequestManager.js.map +1 -1
  49. package/dist/multistream/remoteMedia.js +34 -5
  50. package/dist/multistream/remoteMedia.js.map +1 -1
  51. package/dist/multistream/remoteMediaGroup.js +42 -2
  52. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  53. package/dist/reachability/index.js +3 -3
  54. package/dist/reachability/index.js.map +1 -1
  55. package/dist/types/constants.d.ts +23 -0
  56. package/dist/types/controls-options-manager/index.d.ts +9 -1
  57. package/dist/types/interceptors/index.d.ts +2 -1
  58. package/dist/types/interceptors/locusRouteToken.d.ts +38 -0
  59. package/dist/types/locus-info/index.d.ts +9 -54
  60. package/dist/types/media/properties.d.ts +21 -0
  61. package/dist/types/meeting/in-meeting-actions.d.ts +14 -0
  62. package/dist/types/meeting/index.d.ts +64 -29
  63. package/dist/types/meeting/request.d.ts +42 -0
  64. package/dist/types/meeting/type.d.ts +9 -0
  65. package/dist/types/meeting/util.d.ts +13 -0
  66. package/dist/types/meeting-info/meeting-info-v2.d.ts +6 -3
  67. package/dist/types/meetings/index.d.ts +3 -1
  68. package/dist/types/member/index.d.ts +1 -0
  69. package/dist/types/member/util.d.ts +5 -0
  70. package/dist/types/members/index.d.ts +12 -11
  71. package/dist/types/members/util.d.ts +8 -4
  72. package/dist/types/metrics/constants.d.ts +1 -0
  73. package/dist/types/multistream/remoteMedia.d.ts +20 -1
  74. package/dist/types/multistream/remoteMediaGroup.d.ts +11 -0
  75. package/dist/webinar/index.js +1 -1
  76. package/package.json +25 -27
  77. package/src/constants.ts +26 -2
  78. package/src/controls-options-manager/index.ts +26 -5
  79. package/src/index.ts +2 -1
  80. package/src/interceptors/index.ts +2 -1
  81. package/src/interceptors/locusRouteToken.ts +80 -0
  82. package/src/locus-info/controlsUtils.ts +18 -0
  83. package/src/locus-info/index.ts +69 -357
  84. package/src/locus-info/parser.ts +5 -1
  85. package/src/media/properties.ts +43 -0
  86. package/src/meeting/in-meeting-actions.ts +29 -0
  87. package/src/meeting/index.ts +296 -87
  88. package/src/meeting/request.ts +141 -0
  89. package/src/meeting/type.ts +9 -0
  90. package/src/meeting/util.ts +107 -3
  91. package/src/meeting-info/meeting-info-v2.ts +24 -5
  92. package/src/meetings/index.ts +15 -22
  93. package/src/member/index.ts +10 -0
  94. package/src/member/util.ts +14 -0
  95. package/src/members/index.ts +20 -10
  96. package/src/members/util.ts +20 -3
  97. package/src/metrics/constants.ts +1 -0
  98. package/src/multistream/mediaRequestManager.ts +7 -7
  99. package/src/multistream/remoteMedia.ts +34 -4
  100. package/src/multistream/remoteMediaGroup.ts +37 -2
  101. package/src/reachability/index.ts +3 -3
  102. package/test/unit/spec/common/browser-detection.js +0 -24
  103. package/test/unit/spec/controls-options-manager/index.js +47 -0
  104. package/test/unit/spec/fixture/locus.js +1 -0
  105. package/test/unit/spec/interceptors/locusRouteToken.ts +87 -0
  106. package/test/unit/spec/locus-info/index.js +80 -361
  107. package/test/unit/spec/locus-info/parser.js +3 -2
  108. package/test/unit/spec/media/properties.ts +137 -0
  109. package/test/unit/spec/meeting/in-meeting-actions.ts +14 -0
  110. package/test/unit/spec/meeting/index.js +637 -53
  111. package/test/unit/spec/meeting/muteState.js +32 -6
  112. package/test/unit/spec/meeting/request.js +21 -0
  113. package/test/unit/spec/meeting/utils.js +171 -18
  114. package/test/unit/spec/meeting-info/meetinginfov2.js +8 -3
  115. package/test/unit/spec/meetings/index.js +12 -5
  116. package/test/unit/spec/member/util.js +24 -0
  117. package/test/unit/spec/members/collection.js +120 -0
  118. package/test/unit/spec/members/index.js +107 -2
  119. package/test/unit/spec/members/request.js +55 -0
  120. package/test/unit/spec/members/utils.js +116 -14
  121. package/test/unit/spec/multistream/mediaRequestManager.ts +19 -6
  122. package/test/unit/spec/multistream/remoteMedia.ts +66 -2
  123. package/test/unit/spec/reachability/index.ts +158 -3
  124. package/test/unit/spec/roap/turnDiscovery.ts +3 -3
  125. package/dist/hashTree/constants.js +0 -23
  126. package/dist/hashTree/constants.js.map +0 -1
  127. package/dist/hashTree/hashTree.js +0 -516
  128. package/dist/hashTree/hashTree.js.map +0 -1
  129. package/dist/hashTree/hashTreeParser.js +0 -521
  130. package/dist/hashTree/hashTreeParser.js.map +0 -1
  131. package/dist/types/hashTree/constants.d.ts +0 -8
  132. package/dist/types/hashTree/hashTree.d.ts +0 -128
  133. package/dist/types/hashTree/hashTreeParser.d.ts +0 -152
  134. package/src/hashTree/constants.ts +0 -12
  135. package/src/hashTree/hashTree.ts +0 -460
  136. package/src/hashTree/hashTreeParser.ts +0 -556
  137. package/test/unit/spec/hashTree/hashTree.ts +0 -394
  138. package/test/unit/spec/hashTree/hashTreeParser.ts +0 -156
@@ -14,6 +14,7 @@ import {
14
14
  } from '../constants';
15
15
 
16
16
  import {RoleAssignmentOptions, RoleAssignmentRequest, ServerRoleShape} from './types';
17
+ import {Invitee} from '../meeting/type';
17
18
 
18
19
  const MembersUtil = {
19
20
  /**
@@ -105,7 +106,7 @@ const MembersUtil = {
105
106
  return requestParams;
106
107
  },
107
108
 
108
- isInvalidInvitee: (invitee) => {
109
+ isInvalidInvitee: (invitee: Invitee) => {
109
110
  if (!(invitee && (invitee.email || invitee.emailAddress || invitee.phoneNumber))) {
110
111
  return true;
111
112
  }
@@ -189,13 +190,21 @@ const MembersUtil = {
189
190
  * @param {String} requestingParticipantId id of the participant who is sending request (optional)
190
191
  * @param {String} alias alias name
191
192
  * @param {String} locusUrl url
193
+ * @param {String} suffix optional suffix
192
194
  * @returns {Object} consists of {memberID: string, requestingParticipantId: string, alias: string, locusUrl: string}
193
195
  */
194
- generateEditDisplayNameMemberOptions: (memberId, requestingParticipantId, alias, locusUrl) => ({
196
+ generateEditDisplayNameMemberOptions: (
195
197
  memberId,
196
198
  requestingParticipantId,
197
199
  alias,
198
200
  locusUrl,
201
+ suffix
202
+ ) => ({
203
+ memberId,
204
+ requestingParticipantId,
205
+ alias,
206
+ locusUrl,
207
+ suffix,
199
208
  }),
200
209
 
201
210
  getMuteMemberRequestParams: (options) => {
@@ -300,10 +309,18 @@ const MembersUtil = {
300
309
  * @returns {Object} request parameters (method, uri, body) needed to make a editDisplayName request
301
310
  */
302
311
  editDisplayNameMemberRequestParams: (options) => {
303
- const body = {
312
+ const body: {
313
+ aliasValue: string;
314
+ requestingParticipantId: string;
315
+ suffixValue?: string;
316
+ } = {
304
317
  aliasValue: options.alias,
305
318
  requestingParticipantId: options.requestingParticipantId,
306
319
  };
320
+
321
+ if (options.suffix !== undefined) {
322
+ body.suffixValue = options.suffix;
323
+ }
307
324
  const uri = `${options.locusUrl}/${PARTICIPANT}/${options.memberId}/${ALIAS}`;
308
325
 
309
326
  return {
@@ -86,6 +86,7 @@ const BEHAVIORAL_METRICS = {
86
86
  VERIFY_REGISTRATION_ID_SUCCESS: 'js_sdk_verify_registrationId_success',
87
87
  VERIFY_REGISTRATION_ID_ERROR: 'js_sdk_verify_registrationId_error',
88
88
  JOIN_FORBIDDEN_ERROR: 'js_sdk_join_forbidden_error',
89
+ MEDIA_ISSUE_DETECTED: 'js_sdk_media_issue_detected',
89
90
  };
90
91
 
91
92
  export {BEHAVIORAL_METRICS as default};
@@ -15,7 +15,7 @@ import {cloneDeepWith, debounce, isEmpty} from 'lodash';
15
15
  import LoggerProxy from '../common/logs/logger-proxy';
16
16
 
17
17
  import {ReceiveSlot, ReceiveSlotEvents} from './receiveSlot';
18
- import {getMaxFs} from './remoteMedia';
18
+ import {MAX_FS_VALUES} from './remoteMedia';
19
19
 
20
20
  export interface ActiveSpeakerPolicyInfo {
21
21
  policy: 'active-speaker';
@@ -123,12 +123,12 @@ export class MediaRequestManager {
123
123
 
124
124
  private getDegradedClientRequests(clientRequests: ClientRequestsMap) {
125
125
  const maxFsLimits = [
126
- getMaxFs('best'),
127
- getMaxFs('large'),
128
- getMaxFs('medium'),
129
- getMaxFs('small'),
130
- getMaxFs('very small'),
131
- getMaxFs('thumbnail'),
126
+ MAX_FS_VALUES['1080p'],
127
+ MAX_FS_VALUES['720p'],
128
+ MAX_FS_VALUES['540p'],
129
+ MAX_FS_VALUES['360p'],
130
+ MAX_FS_VALUES['180p'],
131
+ MAX_FS_VALUES['90p'],
132
132
  ];
133
133
 
134
134
  // reduce max-fs until total macroblocks is below limit
@@ -19,17 +19,18 @@ export type RemoteVideoResolution =
19
19
  | 'large' // 1080p or less
20
20
  | 'best'; // highest possible resolution
21
21
 
22
- const MAX_FS_VALUES = {
22
+ export const MAX_FS_VALUES = {
23
23
  '90p': 60,
24
24
  '180p': 240,
25
25
  '360p': 920,
26
+ '540p': 2040,
26
27
  '720p': 3600,
27
28
  '1080p': 8192,
28
29
  };
29
30
 
30
31
  /**
31
32
  * Converts pane size into h264 maxFs
32
- * @param {PaneSize} paneSize
33
+ * @param {RemoteVideoResolution} paneSize
33
34
  * @returns {number}
34
35
  */
35
36
  export function getMaxFs(paneSize: RemoteVideoResolution): number {
@@ -89,6 +90,13 @@ export class RemoteMedia extends EventsScope {
89
90
 
90
91
  public readonly id: RemoteMediaId;
91
92
 
93
+ /**
94
+ * The max frame size of the media request, used for logging and media requests.
95
+ * Set by setSizeHint() based on video element dimensions.
96
+ * When > 0, this value takes precedence over options.resolution in sendMediaRequest().
97
+ */
98
+ private maxFrameSize = 0;
99
+
92
100
  /**
93
101
  * Constructs RemoteMedia instance
94
102
  *
@@ -136,15 +144,34 @@ export class RemoteMedia extends EventsScope {
136
144
  fs = MAX_FS_VALUES['180p'];
137
145
  } else if (height < getThresholdHeight(360)) {
138
146
  fs = MAX_FS_VALUES['360p'];
147
+ } else if (height < getThresholdHeight(540)) {
148
+ fs = MAX_FS_VALUES['540p'];
139
149
  } else if (height <= 720) {
140
150
  fs = MAX_FS_VALUES['720p'];
141
151
  } else {
142
152
  fs = MAX_FS_VALUES['1080p'];
143
153
  }
144
154
 
155
+ this.maxFrameSize = fs;
145
156
  this.receiveSlot?.setMaxFs(fs);
146
157
  }
147
158
 
159
+ /**
160
+ * Get the current effective maxFs value that would be used in media requests
161
+ * @returns {number | undefined} The maxFs value, or undefined if no constraints
162
+ */
163
+ public getEffectiveMaxFs(): number | undefined {
164
+ if (this.maxFrameSize > 0) {
165
+ return this.maxFrameSize;
166
+ }
167
+
168
+ if (this.options.resolution) {
169
+ return getMaxFs(this.options.resolution);
170
+ }
171
+
172
+ return undefined;
173
+ }
174
+
148
175
  /**
149
176
  * Invalidates the remote media by clearing the reference to a receive slot and
150
177
  * cancelling the media request.
@@ -185,6 +212,9 @@ export class RemoteMedia extends EventsScope {
185
212
  throw new Error('sendMediaRequest() called on an invalidated RemoteMedia instance');
186
213
  }
187
214
 
215
+ // Use maxFrameSize from setSizeHint if available, otherwise fallback to options.resolution
216
+ const maxFs = this.getEffectiveMaxFs();
217
+
188
218
  this.mediaRequestId = this.mediaRequestManager.addRequest(
189
219
  {
190
220
  policyInfo: {
@@ -192,9 +222,9 @@ export class RemoteMedia extends EventsScope {
192
222
  csi,
193
223
  },
194
224
  receiveSlots: [this.receiveSlot],
195
- codecInfo: this.options.resolution && {
225
+ codecInfo: maxFs && {
196
226
  codec: 'h264',
197
- maxFs: getMaxFs(this.options.resolution),
227
+ maxFs,
198
228
  },
199
229
  },
200
230
  commit
@@ -215,6 +215,9 @@ export class RemoteMediaGroup {
215
215
  private sendActiveSpeakerMediaRequest(commit: boolean) {
216
216
  this.cancelActiveSpeakerMediaRequest(false);
217
217
 
218
+ // Calculate the effective maxFs based on all unpinned RemoteMedia instances
219
+ const effectiveMaxFs = this.getEffectiveMaxFsForActiveSpeaker();
220
+
218
221
  this.mediaRequestId = this.mediaRequestManager.addRequest(
219
222
  {
220
223
  policyInfo: {
@@ -230,9 +233,9 @@ export class RemoteMediaGroup {
230
233
  receiveSlots: this.unpinnedRemoteMedia.map((remoteMedia) =>
231
234
  remoteMedia.getUnderlyingReceiveSlot()
232
235
  ) as ReceiveSlot[],
233
- codecInfo: this.options.resolution && {
236
+ codecInfo: effectiveMaxFs && {
234
237
  codec: 'h264',
235
- maxFs: getMaxFs(this.options.resolution),
238
+ maxFs: effectiveMaxFs,
236
239
  },
237
240
  },
238
241
  commit
@@ -300,4 +303,36 @@ export class RemoteMediaGroup {
300
303
  this.unpinnedRemoteMedia.includes(remoteMedia) || this.pinnedRemoteMedia.includes(remoteMedia)
301
304
  );
302
305
  }
306
+
307
+ /**
308
+ * Calculate the effective maxFs for the active speaker media request based on unpinned RemoteMedia instances
309
+ * @returns {number | undefined} The calculated maxFs value, or undefined if no constraints
310
+ * @private
311
+ */
312
+ private getEffectiveMaxFsForActiveSpeaker(): number | undefined {
313
+ // Get all effective maxFs values from unpinned RemoteMedia instances
314
+ const maxFsValues = this.unpinnedRemoteMedia
315
+ .map((remoteMedia) => remoteMedia.getEffectiveMaxFs())
316
+ .filter((maxFs) => maxFs !== undefined);
317
+
318
+ // Use the highest maxFs value to ensure we don't under-request resolution for any instance
319
+ if (maxFsValues.length > 0) {
320
+ return Math.max(...maxFsValues);
321
+ }
322
+
323
+ // Fall back to group's resolution option
324
+ if (this.options.resolution) {
325
+ return getMaxFs(this.options.resolution);
326
+ }
327
+
328
+ return undefined;
329
+ }
330
+
331
+ /**
332
+ * Get the current effective maxFs that would be used for the active speaker media request
333
+ * @returns {number | undefined} The effective maxFs value
334
+ */
335
+ public getEffectiveMaxFs(): number | undefined {
336
+ return this.getEffectiveMaxFsForActiveSpeaker();
337
+ }
303
338
  }
@@ -923,10 +923,10 @@ export default class Reachability extends EventsScope {
923
923
 
924
924
  // update expected results counters to include this cluster
925
925
  this.expectedResultsCount[cluster.isVideoMesh ? 'videoMesh' : 'public'].udp +=
926
- cluster.udp.length;
926
+ cluster.udp.length > 0 ? 1 : 0;
927
927
  if (!cluster.isVideoMesh) {
928
- this.expectedResultsCount.public.tcp += cluster.tcp.length;
929
- this.expectedResultsCount.public.xtls += cluster.xtls.length;
928
+ this.expectedResultsCount.public.tcp += cluster.tcp.length > 0 ? 1 : 0;
929
+ this.expectedResultsCount.public.xtls += cluster.xtls.length > 0 ? 1 : 0;
930
930
  }
931
931
  });
932
932
 
@@ -18,16 +18,6 @@ const USER_AGENT_SAFARI_MAC =
18
18
  const USER_AGENT_FIREFOX_MAC =
19
19
  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) ' + 'Gecko/20100101 Firefox/87.0';
20
20
 
21
- const mockDetectionObject = {
22
- /* eslint-disable global-require */
23
- getOSName: () => require('os').platform(),
24
- getOSVersion: () => require('os').release(),
25
- /* eslint-enable global-require */
26
- getBrowserName: () => '',
27
- getBrowserVersion: () => '',
28
- isBrowser: () => false,
29
- };
30
-
31
21
  describe('common/browser-detection', () => {
32
22
  it('returns the correct browser name.', () => {
33
23
  assert.equal(
@@ -102,18 +92,4 @@ describe('common/browser-detection', () => {
102
92
  'This browser is NOT Firefox'
103
93
  );
104
94
  });
105
-
106
- it('returns the mock object when there is no userAgent', () => {
107
- Object.defineProperty(global.window.navigator, 'userAgent', {
108
- get: () => undefined,
109
- configurable: true,
110
- });
111
-
112
- const {getBrowserName, getBrowserVersion, getOSName, getOSVersion} = BrowserDetection(null);
113
-
114
- assert.equal(getBrowserName(), mockDetectionObject.getBrowserName());
115
- assert.equal(getBrowserVersion(), mockDetectionObject.getBrowserVersion());
116
- assert.equal(getOSName(), mockDetectionObject.getOSName());
117
- assert.equal(getOSVersion(), mockDetectionObject.getOSVersion());
118
- });
119
95
  });
@@ -133,6 +133,7 @@ describe('plugin-meetings', () => {
133
133
 
134
134
  manager.set({
135
135
  locusUrl: 'test/id',
136
+ mainLocusUrl: '',
136
137
  displayHints: [],
137
138
  });
138
139
  });
@@ -201,6 +202,38 @@ describe('plugin-meetings', () => {
201
202
  Util.canUpdate = restorable;
202
203
  });
203
204
  });
205
+
206
+ it('should call request with mainLocusUrl and locusUrl as authorizingLocusUrl if mainLocusUrl is exist and not same with locusUrl', () => {
207
+ const restorable = Util.canUpdate;
208
+ Util.canUpdate = sinon.stub().returns(true);
209
+ manager.mainLocusUrl = 'test/main';
210
+
211
+ const audio = {scope: 'audio', properties: {a: 1, b: 2}};
212
+ const reactions = {scope: 'reactions', properties: {c: 3, d: 4}};
213
+
214
+ return manager.update(audio, reactions)
215
+ .then(() => {
216
+ assert.calledWith(request.request, {
217
+ uri: 'test/main/controls',
218
+ body: {
219
+ audio: audio.properties,
220
+ authorizingLocusUrl: 'test/id'
221
+ },
222
+ method: HTTP_VERBS.PATCH,
223
+ });
224
+
225
+ assert.calledWith(request.request, {
226
+ uri: 'test/main/controls',
227
+ body: {
228
+ reactions: reactions.properties,
229
+ authorizingLocusUrl: 'test/id'
230
+ },
231
+ method: HTTP_VERBS.PATCH,
232
+ });
233
+
234
+ Util.canUpdate = restorable;
235
+ });
236
+ });
204
237
  });
205
238
 
206
239
  describe('Mute/Unmute All', () => {
@@ -214,6 +247,7 @@ describe('plugin-meetings', () => {
214
247
 
215
248
  manager.set({
216
249
  locusUrl: 'test/id',
250
+ mainLocusUrl: '',
217
251
  displayHints: [],
218
252
  })
219
253
  });
@@ -305,6 +339,19 @@ describe('plugin-meetings', () => {
305
339
 
306
340
  assert.deepEqual(result, request.request.firstCall.returnValue);
307
341
  });
342
+
343
+ it('request with mainLocusUrl and make locusUrl as authorizingLocusUrl if mainLocusUrl is exist and not same with locusUrl', () => {
344
+ manager.setDisplayHints(['MUTE_ALL', 'DISABLE_HARD_MUTE', 'DISABLE_MUTE_ON_ENTRY']);
345
+ manager.mainLocusUrl = `test/main`;
346
+
347
+ const result = manager.setMuteAll(true, true, true, ['attendee']);
348
+
349
+ assert.calledWith(request.request, { uri: 'test/main/controls',
350
+ body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true, roles: ['attendee'] }, authorizingLocusUrl: 'test/id' },
351
+ method: HTTP_VERBS.PATCH});
352
+
353
+ assert.deepEqual(result, request.request.firstCall.returnValue);
354
+ });
308
355
  });
309
356
  });
310
357
  });
@@ -36,6 +36,7 @@ export default {
36
36
  'LOCK_STATUS_UNLOCKED',
37
37
  'ROSTER_IN_MEETING',
38
38
  'SHARE_WHITEBOARD',
39
+ 'SHOW_AUTO_END_MEETING_WARNING',
39
40
  'SHARE_WHITEBOARD_POLICY',
40
41
  'WEBEX_ASSISTANT_STATUS_INACTIVE',
41
42
  'CAPTION_START',
@@ -0,0 +1,87 @@
1
+ /*!
2
+ * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ /* eslint-disable camelcase */
6
+ import 'jsdom-global/register';
7
+ import {assert} from '@webex/test-helper-chai';
8
+ import MockWebex from '@webex/test-helper-mock-webex';
9
+ import {LocusRouteTokenInterceptor} from '@webex/plugin-meetings/src/interceptors';
10
+ import Meetings from '@webex/plugin-meetings';
11
+
12
+ const X_CISCO_PART_ROUTE_TOKEN = 'X-Cisco-Part-Route-Token';
13
+
14
+ describe('LocusRouteTokenInterceptor', () => {
15
+ let interceptor, webex;
16
+ const TEST_LOCUS_ID = '0f1eba56-91e2-2a11-9b2b-1e2da077f066';
17
+ beforeEach(() => {
18
+ webex = new MockWebex({
19
+ children: {
20
+ meetings: Meetings,
21
+ },
22
+ });
23
+ interceptor = Reflect.apply(LocusRouteTokenInterceptor.create, webex, []);
24
+ });
25
+
26
+ it('getLocusIdByRequestUrl should return locusId from url', () => {
27
+ const url = `https://locus-test.webex.com/locus/api/v1/loci/${TEST_LOCUS_ID}/foo`;
28
+ assert.equal(interceptor.getLocusIdByRequestUrl(url), TEST_LOCUS_ID);
29
+ });
30
+
31
+ it('getLocusIdByRequestUrl should return undefined when no locusId in url', () => {
32
+ const url = 'https://locus-test.webex.com/locus/api/v1/foo';
33
+ assert.isUndefined(interceptor.getLocusIdByRequestUrl(url));
34
+ });
35
+
36
+ it('getLocusIdByRequestUrl should return undefined when url is undefined', () => {
37
+ assert.isUndefined(interceptor.getLocusIdByRequestUrl(undefined));
38
+ });
39
+
40
+ it('onResponse should store route token when header exists', async () => {
41
+ const response = {
42
+ headers: {
43
+ [X_CISCO_PART_ROUTE_TOKEN]: 'test-token',
44
+ },
45
+ };
46
+
47
+ const result = await interceptor.onResponse(
48
+ {
49
+ uri: `https://locus-test.webex.com/locus/api/v1/loci/${TEST_LOCUS_ID}/foo`,
50
+ },
51
+ response
52
+ );
53
+ assert.equal(result, response);
54
+ assert.equal(interceptor.getToken(TEST_LOCUS_ID), 'test-token');
55
+ });
56
+
57
+ it('onResponse should not store token when header missing', async () => {
58
+ interceptor.updateToken(TEST_LOCUS_ID);
59
+ const response = {headers: {}};
60
+
61
+ await interceptor.onResponse({}, response);
62
+ assert.isUndefined(interceptor.getToken(TEST_LOCUS_ID));
63
+ });
64
+
65
+ it('onRequest should attach token to headers when token exists', async () => {
66
+ interceptor.updateToken(TEST_LOCUS_ID, 'abc123');
67
+
68
+ const options = {
69
+ headers: {},
70
+ uri: `https://locus-test.webex.com/locus/api/v1/loci/${TEST_LOCUS_ID}/foo`,
71
+ };
72
+ const result = await interceptor.onRequest(options);
73
+ assert.equal(result.headers[X_CISCO_PART_ROUTE_TOKEN], 'abc123');
74
+ });
75
+
76
+ it('onRequest should not attach token if none is stored', async () => {
77
+ interceptor.updateToken(TEST_LOCUS_ID);
78
+ const options = {headers: {}};
79
+ const result = await interceptor.onRequest(options);
80
+ assert.isUndefined(result.headers[X_CISCO_PART_ROUTE_TOKEN]);
81
+ });
82
+
83
+ it('updateToken & getToken should work as pair', () => {
84
+ interceptor.updateToken(TEST_LOCUS_ID, 'abc456');
85
+ assert.equal(interceptor.getToken(TEST_LOCUS_ID), 'abc456');
86
+ });
87
+ });