@webex/plugin-meetings 1.157.0 → 1.159.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/browsers.js +1 -1
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +172 -36
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/utilv2.js +44 -0
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/index.js +67 -17
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/request.js +16 -6
- package/dist/meetings/request.js.map +1 -1
- package/dist/meetings/util.js +17 -0
- package/dist/meetings/util.js.map +1 -1
- package/package.json +6 -5
- package/src/config.js +2 -1
- package/src/index.js +0 -1
- package/src/meeting-info/meeting-info-v2.js +90 -5
- package/src/meeting-info/utilv2.js +39 -0
- package/src/meetings/index.js +44 -2
- package/src/meetings/request.js +15 -6
- package/src/meetings/util.js +19 -0
- package/test/{unit/spec/transcription/index.js → integration/spec/transcription.js} +1 -1
- package/test/unit/spec/common/browser-detection.js +1 -0
- package/test/unit/spec/locus-info/index.js +1 -1
- package/test/unit/spec/meeting/index.js +43 -3
- package/test/unit/spec/meeting-info/meetinginfov2.js +99 -2
- package/test/unit/spec/meeting-info/utilv2.js +72 -0
- package/test/unit/spec/meetings/index.js +323 -86
- package/test/unit/spec/meetings/utils.js +18 -0
- package/test/unit/spec/members/index.js +1 -0
- package/test/unit/spec/metrics/index.js +7 -26
- package/test/unit/spec/networkQualityMonitor/index.js +1 -0
- package/test/unit/spec/peerconnection-manager/index.js +11 -1
- package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +1 -0
- package/test/unit/spec/reconnection-manager/index.js +1 -0
- package/test/unit/spec/roap/util.js +1 -0
- package/test/unit/spec/stats-analyzer/index.js +1 -0
package/src/config.js
CHANGED
package/src/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
|
|
2
|
-
import {HTTP_VERBS} from '../constants';
|
|
2
|
+
import {HTTP_VERBS, _CONVERSATION_URL_} from '../constants';
|
|
3
3
|
|
|
4
4
|
import MeetingInfoUtil from './utilv2';
|
|
5
5
|
|
|
6
6
|
const PASSWORD_ERROR_DEFAULT_MESSAGE = 'Password required. Call fetchMeetingInfo() with password argument';
|
|
7
7
|
const CAPTCHA_ERROR_DEFAULT_MESSAGE = 'Captcha required. Call fetchMeetingInfo() with captchaInfo argument';
|
|
8
|
+
const ADHOC_MEETING_DEFAULT_ERROR = 'Failed starting the adhoc meeting, Please contact support team ';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Error to indicate that wbxappapi requires a password
|
|
@@ -28,7 +29,27 @@ export class MeetingInfoV2PasswordError extends Error {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
|
-
|
|
32
|
+
* Error generating a adhoc space meeting
|
|
33
|
+
*/
|
|
34
|
+
export class MeetingInfoV2AdhocMeetingError extends Error {
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @constructor
|
|
38
|
+
* @param {Number} [wbxAppApiErrorCode]
|
|
39
|
+
* @param {String} [message]
|
|
40
|
+
*/
|
|
41
|
+
constructor(wbxAppApiErrorCode, message = ADHOC_MEETING_DEFAULT_ERROR) {
|
|
42
|
+
super(`${message}, code=${wbxAppApiErrorCode}`);
|
|
43
|
+
this.name = 'MeetingInfoV2AdhocMeetingError';
|
|
44
|
+
this.sdkMessage = message;
|
|
45
|
+
this.stack = (new Error()).stack;
|
|
46
|
+
this.wbxAppApiCode = wbxAppApiErrorCode;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Error to indicate that preferred webex site not present to start adhoc meeting
|
|
32
53
|
*/
|
|
33
54
|
export class MeetingInfoV2CaptchaError extends Error {
|
|
34
55
|
/**
|
|
@@ -65,7 +86,7 @@ export default class MeetingInfoV2 {
|
|
|
65
86
|
* converts hydra id into conversation url and persons Id
|
|
66
87
|
* @param {String} destination one of many different types of destinations to look up info for
|
|
67
88
|
* @param {String} [type] to match up with the destination value
|
|
68
|
-
* @returns {Promise}
|
|
89
|
+
* @returns {Promise} destination and type
|
|
69
90
|
* @public
|
|
70
91
|
* @memberof MeetingInfo
|
|
71
92
|
*/
|
|
@@ -77,6 +98,59 @@ export default class MeetingInfoV2 {
|
|
|
77
98
|
});
|
|
78
99
|
}
|
|
79
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Creates adhoc space meetings for a space by fetching the conversation infomation
|
|
103
|
+
* @param {String} conversationUrl conversationUrl to start adhoc meeting on
|
|
104
|
+
* @returns {Promise} returns a meeting info object
|
|
105
|
+
* @public
|
|
106
|
+
* @memberof MeetingInfo
|
|
107
|
+
*/
|
|
108
|
+
async createAdhocSpaceMeeting(conversationUrl) {
|
|
109
|
+
if (!this.webex.meetings.preferredWebexSite) {
|
|
110
|
+
throw Error('No preferred webex site found');
|
|
111
|
+
}
|
|
112
|
+
const getInvitees = (particpants = []) => {
|
|
113
|
+
const invitees = [];
|
|
114
|
+
|
|
115
|
+
if (particpants) {
|
|
116
|
+
particpants.forEach((participant) => {
|
|
117
|
+
invitees.push({
|
|
118
|
+
email: participant.emailAddress,
|
|
119
|
+
ciUserUuid: participant.entryUUID
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return invitees;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
return this.webex.internal.conversation.get(
|
|
128
|
+
{url: conversationUrl},
|
|
129
|
+
{includeParticipants: true, disableTransform: true}
|
|
130
|
+
)
|
|
131
|
+
.then((conversation) => {
|
|
132
|
+
const body = {
|
|
133
|
+
title: conversation.displayName,
|
|
134
|
+
spaceUrl: conversation.url,
|
|
135
|
+
keyUrl: conversation.encryptionKeyUrl,
|
|
136
|
+
kroUrl: conversation.kmsResourceObjectUrl,
|
|
137
|
+
invitees: getInvitees(conversation.participants?.items)
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const uri = this.webex.meetings.preferredWebexSite ?
|
|
141
|
+
`https://${this.webex.meetings.preferredWebexSite}/wbxappapi/v2/meetings/spaceInstant` : '';
|
|
142
|
+
|
|
143
|
+
return this.webex.request({
|
|
144
|
+
method: HTTP_VERBS.POST,
|
|
145
|
+
uri,
|
|
146
|
+
body
|
|
147
|
+
})
|
|
148
|
+
.catch((err) => {
|
|
149
|
+
throw new MeetingInfoV2AdhocMeetingError(err.body?.code, err.body?.message);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
80
154
|
/**
|
|
81
155
|
* Fetches meeting info from the server
|
|
82
156
|
* @param {String} destination one of many different types of destinations to look up info for
|
|
@@ -95,14 +169,25 @@ export default class MeetingInfoV2 {
|
|
|
95
169
|
type,
|
|
96
170
|
webex: this.webex
|
|
97
171
|
});
|
|
172
|
+
|
|
173
|
+
if (destinationType.type === _CONVERSATION_URL_ && this.webex.config.meetings.experimental.enableAdhocMeetings) {
|
|
174
|
+
return this.createAdhocSpaceMeeting(destinationType.destination);
|
|
175
|
+
}
|
|
176
|
+
|
|
98
177
|
const body = await MeetingInfoUtil.getRequestBody({...destinationType, password, captchaInfo});
|
|
99
178
|
|
|
100
|
-
|
|
179
|
+
const options = {
|
|
101
180
|
method: HTTP_VERBS.POST,
|
|
102
181
|
service: 'webex-appapi-service',
|
|
103
182
|
resource: 'meetingInfo',
|
|
104
183
|
body
|
|
105
|
-
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const directURI = await MeetingInfoUtil.getDirectMeetingInfoURI(destinationType);
|
|
187
|
+
|
|
188
|
+
if (directURI) options.directURI = directURI;
|
|
189
|
+
|
|
190
|
+
return this.webex.request(options)
|
|
106
191
|
.catch((err) => {
|
|
107
192
|
if (err?.statusCode === 403) {
|
|
108
193
|
throw new MeetingInfoV2PasswordError(err.body?.code, err.body?.data?.meetingInfo);
|
|
@@ -264,4 +264,43 @@ MeetingInfoUtil.getRequestBody = (options) => {
|
|
|
264
264
|
return body;
|
|
265
265
|
};
|
|
266
266
|
|
|
267
|
+
/**
|
|
268
|
+
* Helper function to parse the webex site/host from a URI string.
|
|
269
|
+
* @param {String} uri string (e.g. '10019857020@convergedats.webex.com')
|
|
270
|
+
* @returns {String} the site/host part of the URI string (e.g. 'convergedats.webex.com')
|
|
271
|
+
*/
|
|
272
|
+
MeetingInfoUtil.getWebexSite = (uri) => {
|
|
273
|
+
const exceptedDomains = ['meet.webex.com', 'meetup.webex.com', 'ciscospark.com'];
|
|
274
|
+
const site = uri?.match(/.+@(.+\..+\.*.)$/)?.[1];
|
|
275
|
+
|
|
276
|
+
return exceptedDomains.includes(site) ? null : site;
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Helper function to return the direct URI for fetching meeting info (to avoid a redirect).
|
|
281
|
+
* @param {Object} options type and value to fetch meeting info
|
|
282
|
+
* @param {String} options.type One of [SIP_URI, PERSONAL_ROOM, MEETING_ID, CONVERSATION_URL, LOCUS_ID, MEETING_LINK]
|
|
283
|
+
* @param {Object} options.destination ?? value.value
|
|
284
|
+
* @returns {String} returns a URI string or null of there is no direct URI
|
|
285
|
+
*/
|
|
286
|
+
MeetingInfoUtil.getDirectMeetingInfoURI = (options) => {
|
|
287
|
+
const {
|
|
288
|
+
type, destination
|
|
289
|
+
} = options;
|
|
290
|
+
|
|
291
|
+
let preferredWebexSite = null;
|
|
292
|
+
|
|
293
|
+
switch (type) {
|
|
294
|
+
case _SIP_URI_:
|
|
295
|
+
preferredWebexSite = MeetingInfoUtil.getWebexSite(destination);
|
|
296
|
+
break;
|
|
297
|
+
case _LOCUS_ID_:
|
|
298
|
+
preferredWebexSite = destination.info?.webExSite;
|
|
299
|
+
break;
|
|
300
|
+
default:
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return preferredWebexSite ? `https://${preferredWebexSite}/wbxappapi/v1/meetingInfo` : null;
|
|
304
|
+
};
|
|
305
|
+
|
|
267
306
|
export default MeetingInfoUtil;
|
package/src/meetings/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import '@webex/internal-plugin-mercury';
|
|
6
|
+
import '@webex/internal-plugin-conversation';
|
|
6
7
|
import {WebexPlugin} from '@webex/webex-core';
|
|
7
8
|
|
|
8
9
|
import 'webrtc-adapter';
|
|
@@ -163,6 +164,15 @@ export default class Meetings extends WebexPlugin {
|
|
|
163
164
|
*/
|
|
164
165
|
this.registered = false;
|
|
165
166
|
|
|
167
|
+
/**
|
|
168
|
+
* This values indicates the preferred webex site the user will start there meeting, getsits value from {@link Meetings#register}
|
|
169
|
+
* @instance
|
|
170
|
+
* @type {String}
|
|
171
|
+
* @private
|
|
172
|
+
* @memberof Meetings
|
|
173
|
+
*/
|
|
174
|
+
this.preferredWebexSite = '';
|
|
175
|
+
|
|
166
176
|
/**
|
|
167
177
|
* The public interface for the internal Media util files. These are helpful to expose outside the context
|
|
168
178
|
* of a meeting so that a user can access media without creating a meeting instance.
|
|
@@ -176,8 +186,6 @@ export default class Meetings extends WebexPlugin {
|
|
|
176
186
|
getSupportedDevice: Media.getSupportedDevice
|
|
177
187
|
};
|
|
178
188
|
|
|
179
|
-
LoggerProxy.set(this.webex.logger);
|
|
180
|
-
|
|
181
189
|
this.onReady();
|
|
182
190
|
MeetingsUtil.checkH264Support({disableNotifications: true});
|
|
183
191
|
Metrics.initialSetup(this.meetingCollection, this.webex);
|
|
@@ -368,6 +376,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
368
376
|
onReady() {
|
|
369
377
|
this.webex.once(READY, () => {
|
|
370
378
|
StaticConfig.set(this.config);
|
|
379
|
+
LoggerProxy.set(this.webex.logger);
|
|
371
380
|
LoggerConfig.set(this.config.logging);
|
|
372
381
|
|
|
373
382
|
/**
|
|
@@ -408,6 +417,22 @@ export default class Meetings extends WebexPlugin {
|
|
|
408
417
|
}
|
|
409
418
|
}
|
|
410
419
|
|
|
420
|
+
/**
|
|
421
|
+
* API to toggle starting adhoc meeting
|
|
422
|
+
* @param {Boolean} changeState
|
|
423
|
+
* @private
|
|
424
|
+
* @memberof Meetings
|
|
425
|
+
* @returns {undefined}
|
|
426
|
+
*/
|
|
427
|
+
_toggleAdhocMeetings(changeState) {
|
|
428
|
+
if (typeof changeState !== 'boolean') {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
if (this.config?.experimental?.enableAdhocMeetings !== changeState) {
|
|
432
|
+
this.config.experimental.enableAdhocMeetings = changeState;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
411
436
|
/**
|
|
412
437
|
* Explicitly sets up the meetings plugin by registering
|
|
413
438
|
* the device, connecting to mercury, and listening for locus events.
|
|
@@ -431,6 +456,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
431
456
|
}
|
|
432
457
|
|
|
433
458
|
return Promise.all([
|
|
459
|
+
this.fetchUserPreferredWebexSite(),
|
|
434
460
|
this.getGeoHint(),
|
|
435
461
|
this.startReachability().catch((error) => {
|
|
436
462
|
LoggerProxy.logger.error(`Meetings:index#register --> GDM error, ${error.message}`);
|
|
@@ -605,12 +631,28 @@ export default class Meetings extends WebexPlugin {
|
|
|
605
631
|
});
|
|
606
632
|
}
|
|
607
633
|
|
|
634
|
+
/**
|
|
635
|
+
* Fetch user preferred webex site information
|
|
636
|
+
* This also has other infomation about the user
|
|
637
|
+
* @returns {Promise}
|
|
638
|
+
* @private
|
|
639
|
+
* @memberof Meetings
|
|
640
|
+
*/
|
|
641
|
+
fetchUserPreferredWebexSite() {
|
|
642
|
+
return this.request.fetchLoginUserInformation().then((res) => {
|
|
643
|
+
this.preferredWebexSite = MeetingsUtil.parseUserPreferences(res.userPreferences);
|
|
644
|
+
console.error(this.preferredWebexSite);
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
|
|
608
649
|
/**
|
|
609
650
|
* gets the personal meeting room instance, for saved PMR values for this user
|
|
610
651
|
* @returns {PersonalMeetingRoom}
|
|
611
652
|
* @public
|
|
612
653
|
* @memberof Meetings
|
|
613
654
|
*/
|
|
655
|
+
|
|
614
656
|
getPersonalMeetingRoom() {
|
|
615
657
|
return this.personalMeetingRoom;
|
|
616
658
|
}
|
package/src/meetings/request.js
CHANGED
|
@@ -12,9 +12,9 @@ import {
|
|
|
12
12
|
*/
|
|
13
13
|
export default class MeetingRequest extends StatelessWebexPlugin {
|
|
14
14
|
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
* get all the active meetings for the user
|
|
16
|
+
* @returns {Array} return locus array
|
|
17
|
+
*/
|
|
18
18
|
getActiveMeetings() {
|
|
19
19
|
return this.request({
|
|
20
20
|
api: API.LOCUS,
|
|
@@ -27,13 +27,22 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
* fetch geoHit for the user
|
|
31
|
+
* @returns {Promise<object>} geoHintInfo
|
|
32
|
+
*/
|
|
33
33
|
fetchGeoHint() {
|
|
34
34
|
return this.webex.internal.services.fetchClientRegionInfo();
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* fetch login user information
|
|
39
|
+
* @returns {Promise<object>} loginUserInformation
|
|
40
|
+
*/
|
|
41
|
+
fetchLoginUserInformation() {
|
|
42
|
+
return this.webex.internal.services.fetchLoginUserInformation();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
37
46
|
// locus federation, determines and populate locus if the responseBody has remote URLs to fetch locus details
|
|
38
47
|
|
|
39
48
|
/**
|
package/src/meetings/util.js
CHANGED
|
@@ -64,6 +64,25 @@ MeetingsUtil.checkForCorrelationId = (deviceUrl, locus) => {
|
|
|
64
64
|
return false;
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
+
MeetingsUtil.parseUserPreferences = (userPreferences) => {
|
|
68
|
+
let webexSite = null;
|
|
69
|
+
|
|
70
|
+
userPreferences.find((item) => {
|
|
71
|
+
// eslint-disable-next-line no-useless-escape
|
|
72
|
+
const regex = /"preferredWebExSite\":\"(\S+)\"/;
|
|
73
|
+
const preferredSite = item.match(regex);
|
|
74
|
+
|
|
75
|
+
if (preferredSite) {
|
|
76
|
+
webexSite = preferredSite[1];
|
|
77
|
+
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return false;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return webexSite;
|
|
85
|
+
};
|
|
67
86
|
|
|
68
87
|
/**
|
|
69
88
|
* Will check to see if the H.264 media codec is supported.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import Transcription from '@webex/plugin-meetings/src/transcription';
|
|
1
2
|
import {assert} from '@webex/test-helper-chai';
|
|
2
3
|
import sinon from 'sinon';
|
|
3
|
-
import Transcription from '@webex/plugin-meetings/src/transcription';
|
|
4
4
|
|
|
5
5
|
describe('transcription index', () => {
|
|
6
6
|
let webSocketUrl, members, sessionId, token, transcription;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import 'jsdom-global/register';
|
|
1
2
|
import sinon from 'sinon';
|
|
2
3
|
import {cloneDeep} from 'lodash';
|
|
3
4
|
import {assert} from '@webex/test-helper-chai';
|
|
@@ -16,7 +17,6 @@ import {
|
|
|
16
17
|
|
|
17
18
|
import {self, selfWithInactivity} from './selfConstant';
|
|
18
19
|
|
|
19
|
-
|
|
20
20
|
describe('plugin-meetings', () => {
|
|
21
21
|
describe('LocusInfo index', () => {
|
|
22
22
|
const updateMeeting = () => {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
3
|
*/
|
|
4
|
+
import 'jsdom-global/register';
|
|
4
5
|
import {cloneDeep, isEqual} from 'lodash';
|
|
5
6
|
import sinon from 'sinon';
|
|
6
7
|
import StateMachine from 'javascript-state-machine';
|
|
@@ -37,7 +38,7 @@ import {
|
|
|
37
38
|
EVENT_TRIGGERS,
|
|
38
39
|
_SIP_URI_
|
|
39
40
|
} from '@webex/plugin-meetings/src/constants';
|
|
40
|
-
import
|
|
41
|
+
import BEHAVIORAL_METRICS from '@webex/plugin-meetings/src/metrics/constants';
|
|
41
42
|
|
|
42
43
|
import {
|
|
43
44
|
UserNotJoinedError,
|
|
@@ -72,6 +73,43 @@ describe('plugin-meetings', () => {
|
|
|
72
73
|
};
|
|
73
74
|
|
|
74
75
|
before(() => {
|
|
76
|
+
const MediaStream = {
|
|
77
|
+
getVideoTracks: () => [{
|
|
78
|
+
applyConstraints: () => { }
|
|
79
|
+
}]
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
Object.defineProperty(global.window.navigator, 'mediaDevices', {
|
|
83
|
+
writable: true,
|
|
84
|
+
value: {
|
|
85
|
+
getDisplayMedia: sinon.stub().returns(Promise.resolve(MediaStream)),
|
|
86
|
+
enumerateDevices: sinon.stub().returns(Promise.resolve([
|
|
87
|
+
{
|
|
88
|
+
deviceId: '',
|
|
89
|
+
kind: 'audioinput',
|
|
90
|
+
label: '',
|
|
91
|
+
groupId: '29d9339cc77bffdd24cb69ee80f6d3200481099bcd0f29267558672de0430777',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
deviceId: '',
|
|
95
|
+
kind: 'videoinput',
|
|
96
|
+
label: '',
|
|
97
|
+
groupId: '08d4f8200e7e4a3425ecf75b7edea9ae4acd934019f2a52217554bcc8e46604d',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
deviceId: '',
|
|
101
|
+
kind: 'audiooutput',
|
|
102
|
+
label: '',
|
|
103
|
+
groupId: '29d9339cc77bffdd24cb69ee80f6d3200481099bcd0f29267558672de0430777',
|
|
104
|
+
}
|
|
105
|
+
])),
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
Object.defineProperty(global.window, 'MediaStream', {
|
|
110
|
+
writable: true,
|
|
111
|
+
value: MediaStream
|
|
112
|
+
});
|
|
75
113
|
LoggerConfig.set({verboseEvents: true, enable: false});
|
|
76
114
|
LoggerProxy.set(logger);
|
|
77
115
|
});
|
|
@@ -108,6 +146,9 @@ describe('plugin-meetings', () => {
|
|
|
108
146
|
metrics: {},
|
|
109
147
|
stats: {},
|
|
110
148
|
experimental: {enableUnifiedMeetings: true}
|
|
149
|
+
},
|
|
150
|
+
metrics: {
|
|
151
|
+
type: ['behavioral']
|
|
111
152
|
}
|
|
112
153
|
}
|
|
113
154
|
});
|
|
@@ -122,6 +163,7 @@ describe('plugin-meetings', () => {
|
|
|
122
163
|
Metrics.postEvent = sinon.stub();
|
|
123
164
|
Metrics.initialSetup(null, webex);
|
|
124
165
|
MediaUtil.createPeerConnection = sinon.stub().returns({});
|
|
166
|
+
MediaUtil.createMediaStream = sinon.stub().returns(true);
|
|
125
167
|
|
|
126
168
|
uuid1 = uuid.v4();
|
|
127
169
|
uuid2 = uuid.v4();
|
|
@@ -2530,7 +2572,6 @@ describe('plugin-meetings', () => {
|
|
|
2530
2572
|
sandbox.stub(MeetingUtil, 'getTrack').returns({audioTrack: null, videoTrack: fakeTrack});
|
|
2531
2573
|
sandbox.stub(meeting.mediaProperties, 'setMediaSettings');
|
|
2532
2574
|
sandbox.stub(meeting.mediaProperties, 'setVideoDeviceId');
|
|
2533
|
-
sandbox.stub(MediaUtil, 'createMediaStream').returns(true);
|
|
2534
2575
|
|
|
2535
2576
|
meeting.setLocalTracks(fakeStream);
|
|
2536
2577
|
|
|
@@ -2571,7 +2612,6 @@ describe('plugin-meetings', () => {
|
|
|
2571
2612
|
});
|
|
2572
2613
|
describe('#setRemoteStream', () => {
|
|
2573
2614
|
beforeEach(() => {
|
|
2574
|
-
MediaUtil.createMediaStream = sinon.stub().returns(true);
|
|
2575
2615
|
meeting.statsAnalyzer = {startAnalyzer: sinon.stub()};
|
|
2576
2616
|
});
|
|
2577
2617
|
it('should trigger a media:ready event when remote stream track ontrack is fired', () => {
|
|
@@ -10,12 +10,34 @@ import Mercury from '@webex/internal-plugin-mercury';
|
|
|
10
10
|
import Meetings from '@webex/plugin-meetings/src/meetings';
|
|
11
11
|
import {
|
|
12
12
|
_MEETING_ID_,
|
|
13
|
-
_PERSONAL_ROOM_
|
|
13
|
+
_PERSONAL_ROOM_,
|
|
14
|
+
_CONVERSATION_URL_
|
|
14
15
|
} from '@webex/plugin-meetings/src/constants';
|
|
15
|
-
import MeetingInfo, {MeetingInfoV2PasswordError, MeetingInfoV2CaptchaError} from '@webex/plugin-meetings/src/meeting-info/meeting-info-v2';
|
|
16
|
+
import MeetingInfo, {MeetingInfoV2PasswordError, MeetingInfoV2CaptchaError, MeetingInfoV2AdhocMeetingError} from '@webex/plugin-meetings/src/meeting-info/meeting-info-v2';
|
|
16
17
|
import MeetingInfoUtil from '@webex/plugin-meetings/src/meeting-info/utilv2';
|
|
17
18
|
|
|
18
19
|
describe('plugin-meetings', () => {
|
|
20
|
+
const conversation = {
|
|
21
|
+
displayName: 'displayName',
|
|
22
|
+
url: 'conversationUrl',
|
|
23
|
+
encryptionKeyUrl: 'encryptionKeyUrl',
|
|
24
|
+
kmsResourceObjectUrl: 'kmsResourceObjectUrl',
|
|
25
|
+
participants: {
|
|
26
|
+
items: [
|
|
27
|
+
{
|
|
28
|
+
id: '344ea183-9d5d-4e77-aed',
|
|
29
|
+
emailAddress: 'testUser1@cisco.com',
|
|
30
|
+
entryUUID: '344ea183-9d5d-4e77-'
|
|
31
|
+
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: '40b446fe-175c-4628-8a9d',
|
|
35
|
+
emailAddress: 'testUser2@cisco.com',
|
|
36
|
+
entryUUID: '40b446fe-175c-4628'
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
};
|
|
19
41
|
let webex;
|
|
20
42
|
let meetingInfo = null;
|
|
21
43
|
|
|
@@ -29,6 +51,9 @@ describe('plugin-meetings', () => {
|
|
|
29
51
|
}
|
|
30
52
|
});
|
|
31
53
|
|
|
54
|
+
webex.meetings.preferredWebexSite = 'go.webex.com';
|
|
55
|
+
webex.config.meetings = {experimental: {enableUnifiedMeetings: true, enableAdhocMeetings: true}};
|
|
56
|
+
|
|
32
57
|
Object.assign(webex.internal, {
|
|
33
58
|
device: {
|
|
34
59
|
deviceType: 'FAKE_DEVICE',
|
|
@@ -41,6 +66,9 @@ describe('plugin-meetings', () => {
|
|
|
41
66
|
disconnect: sinon.stub().returns(Promise.resolve()),
|
|
42
67
|
on: () => {},
|
|
43
68
|
off: () => {}
|
|
69
|
+
},
|
|
70
|
+
conversation: {
|
|
71
|
+
get: sinon.stub().returns(Promise.resolve(conversation))
|
|
44
72
|
}
|
|
45
73
|
});
|
|
46
74
|
|
|
@@ -98,6 +126,40 @@ describe('plugin-meetings', () => {
|
|
|
98
126
|
});
|
|
99
127
|
});
|
|
100
128
|
|
|
129
|
+
it('create adhoc meeting when conversationUrl passed with enableAdhocMeetings toggle', async () => {
|
|
130
|
+
sinon.stub(meetingInfo, 'createAdhocSpaceMeeting').returns(Promise.resolve());
|
|
131
|
+
await meetingInfo.fetchMeetingInfo('conversationUrl', _CONVERSATION_URL_);
|
|
132
|
+
|
|
133
|
+
assert.calledWith(meetingInfo.createAdhocSpaceMeeting, 'conversationUrl');
|
|
134
|
+
assert.notCalled(webex.request);
|
|
135
|
+
meetingInfo.createAdhocSpaceMeeting.restore();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should not call createAdhocSpaceMeeting if enableAdhocMeetings toggle is off', async () => {
|
|
139
|
+
webex.config.meetings.experimental.enableAdhocMeetings = false;
|
|
140
|
+
sinon.stub(meetingInfo, 'createAdhocSpaceMeeting').returns(Promise.resolve());
|
|
141
|
+
|
|
142
|
+
await meetingInfo.fetchMeetingInfo('conversationUrl', _CONVERSATION_URL_);
|
|
143
|
+
|
|
144
|
+
assert.notCalled(meetingInfo.createAdhocSpaceMeeting);
|
|
145
|
+
assert.called(webex.request);
|
|
146
|
+
meetingInfo.createAdhocSpaceMeeting.restore();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should throw an error MeetingInfoV2AdhocMeetingError if not able to start adhoc meeting for a conversation', async () => {
|
|
150
|
+
webex.config.meetings.experimental.enableAdhocMeetings = true;
|
|
151
|
+
|
|
152
|
+
webex.request = sinon.stub().rejects({statusCode: 403, body: {code: 400000, message: 'Input is invalid'}});
|
|
153
|
+
try {
|
|
154
|
+
await meetingInfo.createAdhocSpaceMeeting('conversationUrl');
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
assert.instanceOf(err, MeetingInfoV2AdhocMeetingError);
|
|
158
|
+
assert.deepEqual(err.message, 'Input is invalid, code=400000');
|
|
159
|
+
assert.equal(err.wbxAppApiCode, 400000);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
101
163
|
it('should throw MeetingInfoV2PasswordError for 403 response', async () => {
|
|
102
164
|
const FAKE_MEETING_INFO = {blablabla: 'some_fake_meeting_info'};
|
|
103
165
|
|
|
@@ -154,5 +216,40 @@ describe('plugin-meetings', () => {
|
|
|
154
216
|
});
|
|
155
217
|
});
|
|
156
218
|
});
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
describe('createAdhocSpaceMeeting', () => {
|
|
222
|
+
it('Make a request to /instantSpace when conversationUrl', async () => {
|
|
223
|
+
const conversationUrl = 'https://conversationUrl/xxx';
|
|
224
|
+
const invitee = [];
|
|
225
|
+
|
|
226
|
+
invitee.push({
|
|
227
|
+
email: conversation.participants.items[0].emailAddress,
|
|
228
|
+
ciUserUuid: conversation.participants.items[0].entryUUID
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
invitee.push({
|
|
232
|
+
email: conversation.participants.items[1].emailAddress,
|
|
233
|
+
ciUserUuid: conversation.participants.items[1].entryUUID
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
await meetingInfo.createAdhocSpaceMeeting(conversationUrl);
|
|
237
|
+
|
|
238
|
+
assert.calledWith(webex.internal.conversation.get, {url: conversationUrl},
|
|
239
|
+
{includeParticipants: true, disableTransform: true});
|
|
240
|
+
|
|
241
|
+
assert.calledWith(webex.request, {
|
|
242
|
+
method: 'POST',
|
|
243
|
+
uri: 'https://go.webex.com/wbxappapi/v2/meetings/spaceInstant',
|
|
244
|
+
body: {
|
|
245
|
+
title: conversation.displayName,
|
|
246
|
+
spaceUrl: conversation.url,
|
|
247
|
+
keyUrl: conversation.encryptionKeyUrl,
|
|
248
|
+
kroUrl: conversation.kmsResourceObjectUrl,
|
|
249
|
+
invitees: invitee
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
});
|
|
157
254
|
});
|
|
158
255
|
});
|