@webex/plugin-meetings 1.158.0 → 1.159.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/meeting/index.js +23 -7
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +165 -38
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +78 -31
- 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/meeting/index.js +21 -6
- package/src/meeting-info/meeting-info-v2.js +82 -3
- package/src/meetings/index.js +48 -4
- package/src/meetings/request.js +15 -6
- package/src/meetings/util.js +19 -0
- package/test/unit/spec/meeting/index.js +41 -13
- package/test/unit/spec/meeting-info/meetinginfov2.js +99 -2
- package/test/unit/spec/meetings/index.js +90 -29
- package/test/unit/spec/meetings/utils.js +18 -0
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.
|
|
@@ -407,6 +417,22 @@ export default class Meetings extends WebexPlugin {
|
|
|
407
417
|
}
|
|
408
418
|
}
|
|
409
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
|
+
|
|
410
436
|
/**
|
|
411
437
|
* Explicitly sets up the meetings plugin by registering
|
|
412
438
|
* the device, connecting to mercury, and listening for locus events.
|
|
@@ -430,6 +456,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
430
456
|
}
|
|
431
457
|
|
|
432
458
|
return Promise.all([
|
|
459
|
+
this.fetchUserPreferredWebexSite(),
|
|
433
460
|
this.getGeoHint(),
|
|
434
461
|
this.startReachability().catch((error) => {
|
|
435
462
|
LoggerProxy.logger.error(`Meetings:index#register --> GDM error, ${error.message}`);
|
|
@@ -604,12 +631,29 @@ export default class Meetings extends WebexPlugin {
|
|
|
604
631
|
});
|
|
605
632
|
}
|
|
606
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
|
+
if (res && res.userPreferences) {
|
|
644
|
+
this.preferredWebexSite = MeetingsUtil.parseUserPreferences(res?.userPreferences);
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
|
|
607
650
|
/**
|
|
608
651
|
* gets the personal meeting room instance, for saved PMR values for this user
|
|
609
652
|
* @returns {PersonalMeetingRoom}
|
|
610
653
|
* @public
|
|
611
654
|
* @memberof Meetings
|
|
612
655
|
*/
|
|
656
|
+
|
|
613
657
|
getPersonalMeetingRoom() {
|
|
614
658
|
return this.personalMeetingRoom;
|
|
615
659
|
}
|
|
@@ -740,7 +784,9 @@ export default class Meetings extends WebexPlugin {
|
|
|
740
784
|
orgId: this.webex.internal.device.orgId,
|
|
741
785
|
roapSeq: 0,
|
|
742
786
|
locus: type === _LOCUS_ID_ ? destination : null, // pass the locus object if present
|
|
743
|
-
meetingInfoProvider: this.meetingInfo
|
|
787
|
+
meetingInfoProvider: this.meetingInfo,
|
|
788
|
+
destination,
|
|
789
|
+
destinationType: type,
|
|
744
790
|
},
|
|
745
791
|
{
|
|
746
792
|
parent: this.webex
|
|
@@ -750,7 +796,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
750
796
|
this.meetingCollection.set(meeting);
|
|
751
797
|
|
|
752
798
|
try {
|
|
753
|
-
await meeting.fetchMeetingInfo({
|
|
799
|
+
await meeting.fetchMeetingInfo({});
|
|
754
800
|
}
|
|
755
801
|
catch (err) {
|
|
756
802
|
if (!(err instanceof CaptchaError) && !(err instanceof PasswordError)) {
|
|
@@ -759,8 +805,6 @@ export default class Meetings extends WebexPlugin {
|
|
|
759
805
|
LoggerProxy.logger.info('Meetings:index#createMeeting --> Info assuming this destination is a 1:1 or wireless share');
|
|
760
806
|
}
|
|
761
807
|
LoggerProxy.logger.debug(`Meetings:index#createMeeting --> Debug ${err} fetching /meetingInfo for creation.`);
|
|
762
|
-
// We need to save this info for future reference
|
|
763
|
-
meeting.destination = destination;
|
|
764
808
|
}
|
|
765
809
|
finally {
|
|
766
810
|
// For type LOCUS_ID we need to parse the locus object to get the information
|
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.
|
|
@@ -36,7 +36,8 @@ import {
|
|
|
36
36
|
PASSWORD_STATUS,
|
|
37
37
|
EVENTS,
|
|
38
38
|
EVENT_TRIGGERS,
|
|
39
|
-
_SIP_URI_
|
|
39
|
+
_SIP_URI_,
|
|
40
|
+
_MEETING_ID_,
|
|
40
41
|
} from '@webex/plugin-meetings/src/constants';
|
|
41
42
|
import BEHAVIORAL_METRICS from '@webex/plugin-meetings/src/metrics/constants';
|
|
42
43
|
|
|
@@ -126,6 +127,7 @@ describe('plugin-meetings', () => {
|
|
|
126
127
|
let test2;
|
|
127
128
|
let test3;
|
|
128
129
|
let test4;
|
|
130
|
+
let testDestination;
|
|
129
131
|
|
|
130
132
|
beforeEach(() => {
|
|
131
133
|
webex = new MockWebex({
|
|
@@ -175,13 +177,16 @@ describe('plugin-meetings', () => {
|
|
|
175
177
|
test2 = `test2-${uuid.v4()}`;
|
|
176
178
|
test3 = `test3-${uuid.v4()}`;
|
|
177
179
|
test4 = `test4-${uuid.v4()}`;
|
|
180
|
+
testDestination = `testDestination-${uuid.v4()}`;
|
|
178
181
|
|
|
179
182
|
meeting = new Meeting(
|
|
180
183
|
{
|
|
181
184
|
userId: uuid1,
|
|
182
185
|
resource: uuid2,
|
|
183
186
|
deviceUrl: uuid3,
|
|
184
|
-
locus: {url: url1}
|
|
187
|
+
locus: {url: url1},
|
|
188
|
+
destination: testDestination,
|
|
189
|
+
destinationType: _MEETING_ID_,
|
|
185
190
|
},
|
|
186
191
|
{
|
|
187
192
|
parent: webex
|
|
@@ -227,6 +232,8 @@ describe('plugin-meetings', () => {
|
|
|
227
232
|
assert.equal(meeting.passwordStatus, PASSWORD_STATUS.UNKNOWN);
|
|
228
233
|
assert.equal(meeting.requiredCaptcha, null);
|
|
229
234
|
assert.equal(meeting.meetingInfoFailureReason, undefined);
|
|
235
|
+
assert.equal(meeting.destination, testDestination);
|
|
236
|
+
assert.equal(meeting.destinationType, _MEETING_ID_);
|
|
230
237
|
});
|
|
231
238
|
});
|
|
232
239
|
describe('#invite', () => {
|
|
@@ -2141,8 +2148,11 @@ describe('plugin-meetings', () => {
|
|
|
2141
2148
|
it('calls meetingInfoProvider with all the right parameters and parses the result', async () => {
|
|
2142
2149
|
meeting.attrs.meetingInfoProvider = {fetchMeetingInfo: sinon.stub().resolves({body: FAKE_MEETING_INFO})};
|
|
2143
2150
|
meeting.requiredCaptcha = FAKE_SDK_CAPTCHA_INFO;
|
|
2151
|
+
meeting.destination = FAKE_DESTINATION;
|
|
2152
|
+
meeting.destinationType = FAKE_TYPE;
|
|
2153
|
+
|
|
2144
2154
|
await meeting.fetchMeetingInfo({
|
|
2145
|
-
|
|
2155
|
+
password: FAKE_PASSWORD, captchaCode: FAKE_CAPTCHA_CODE
|
|
2146
2156
|
});
|
|
2147
2157
|
|
|
2148
2158
|
assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, FAKE_PASSWORD, {code: FAKE_CAPTCHA_CODE, id: FAKE_CAPTCHA_ID});
|
|
@@ -2156,9 +2166,11 @@ describe('plugin-meetings', () => {
|
|
|
2156
2166
|
it('fails if captchaCode is provided when captcha not needed', async () => {
|
|
2157
2167
|
meeting.attrs.meetingInfoProvider = {fetchMeetingInfo: sinon.stub().resolves({body: FAKE_MEETING_INFO})};
|
|
2158
2168
|
meeting.requiredCaptcha = null;
|
|
2169
|
+
meeting.destination = FAKE_DESTINATION;
|
|
2170
|
+
meeting.destinationType = FAKE_TYPE;
|
|
2159
2171
|
|
|
2160
2172
|
await assert.isRejected(meeting.fetchMeetingInfo({
|
|
2161
|
-
|
|
2173
|
+
captchaCode: FAKE_CAPTCHA_CODE
|
|
2162
2174
|
}), Error, 'fetchMeetingInfo() called with captchaCode when captcha was not required');
|
|
2163
2175
|
|
|
2164
2176
|
assert.notCalled(meeting.attrs.meetingInfoProvider.fetchMeetingInfo);
|
|
@@ -2167,22 +2179,24 @@ describe('plugin-meetings', () => {
|
|
|
2167
2179
|
it('fails if password is provided when not required', async () => {
|
|
2168
2180
|
meeting.attrs.meetingInfoProvider = {fetchMeetingInfo: sinon.stub().resolves({body: FAKE_MEETING_INFO})};
|
|
2169
2181
|
meeting.passwordStatus = PASSWORD_STATUS.NOT_REQUIRED;
|
|
2182
|
+
meeting.destination = FAKE_DESTINATION;
|
|
2183
|
+
meeting.destinationType = FAKE_TYPE;
|
|
2170
2184
|
|
|
2171
2185
|
await assert.isRejected(meeting.fetchMeetingInfo({
|
|
2172
|
-
|
|
2186
|
+
password: FAKE_PASSWORD
|
|
2173
2187
|
}), Error, 'fetchMeetingInfo() called with password when password was not required');
|
|
2174
2188
|
|
|
2175
2189
|
assert.notCalled(meeting.attrs.meetingInfoProvider.fetchMeetingInfo);
|
|
2176
2190
|
});
|
|
2177
2191
|
|
|
2178
2192
|
it('handles meetingInfoProvider requiring password', async () => {
|
|
2193
|
+
meeting.destination = FAKE_DESTINATION;
|
|
2194
|
+
meeting.destinationType = FAKE_TYPE;
|
|
2179
2195
|
meeting.attrs.meetingInfoProvider = {
|
|
2180
2196
|
fetchMeetingInfo: sinon.stub().throws(new MeetingInfoV2PasswordError(403004, FAKE_MEETING_INFO))
|
|
2181
2197
|
};
|
|
2182
2198
|
|
|
2183
|
-
await assert.isRejected(meeting.fetchMeetingInfo({
|
|
2184
|
-
destination: FAKE_DESTINATION, type: FAKE_TYPE
|
|
2185
|
-
}), PasswordError);
|
|
2199
|
+
await assert.isRejected(meeting.fetchMeetingInfo({}), PasswordError);
|
|
2186
2200
|
|
|
2187
2201
|
assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, null, null);
|
|
2188
2202
|
|
|
@@ -2193,13 +2207,15 @@ describe('plugin-meetings', () => {
|
|
|
2193
2207
|
});
|
|
2194
2208
|
|
|
2195
2209
|
it('handles meetingInfoProvider requiring captcha because of wrong password', async () => {
|
|
2210
|
+
meeting.destination = FAKE_DESTINATION;
|
|
2211
|
+
meeting.destinationType = FAKE_TYPE;
|
|
2196
2212
|
meeting.attrs.meetingInfoProvider = {
|
|
2197
2213
|
fetchMeetingInfo: sinon.stub().throws(new MeetingInfoV2CaptchaError(423005, FAKE_SDK_CAPTCHA_INFO))
|
|
2198
2214
|
};
|
|
2199
2215
|
meeting.requiredCaptcha = null;
|
|
2200
2216
|
|
|
2201
2217
|
await assert.isRejected(meeting.fetchMeetingInfo({
|
|
2202
|
-
|
|
2218
|
+
password: 'aaa'
|
|
2203
2219
|
}), CaptchaError);
|
|
2204
2220
|
|
|
2205
2221
|
assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, 'aaa', null);
|
|
@@ -2216,13 +2232,15 @@ describe('plugin-meetings', () => {
|
|
|
2216
2232
|
});
|
|
2217
2233
|
|
|
2218
2234
|
it('handles meetingInfoProvider requiring captcha because of wrong captcha', async () => {
|
|
2235
|
+
meeting.destination = FAKE_DESTINATION;
|
|
2236
|
+
meeting.destinationType = FAKE_TYPE;
|
|
2219
2237
|
meeting.attrs.meetingInfoProvider = {
|
|
2220
2238
|
fetchMeetingInfo: sinon.stub().throws(new MeetingInfoV2CaptchaError(423005, FAKE_SDK_CAPTCHA_INFO))
|
|
2221
2239
|
};
|
|
2222
2240
|
meeting.requiredCaptcha = FAKE_SDK_CAPTCHA_INFO;
|
|
2223
2241
|
|
|
2224
2242
|
await assert.isRejected(meeting.fetchMeetingInfo({
|
|
2225
|
-
|
|
2243
|
+
password: 'aaa', captchaCode: 'bbb'
|
|
2226
2244
|
}), CaptchaError);
|
|
2227
2245
|
|
|
2228
2246
|
assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, 'aaa', {code: 'bbb', id: FAKE_CAPTCHA_ID});
|
|
@@ -2234,6 +2252,8 @@ describe('plugin-meetings', () => {
|
|
|
2234
2252
|
});
|
|
2235
2253
|
|
|
2236
2254
|
it('handles successful response when good password is passed', async () => {
|
|
2255
|
+
meeting.destination = FAKE_DESTINATION;
|
|
2256
|
+
meeting.destinationType = FAKE_TYPE;
|
|
2237
2257
|
meeting.attrs.meetingInfoProvider = {
|
|
2238
2258
|
fetchMeetingInfo: sinon.stub().resolves(
|
|
2239
2259
|
{
|
|
@@ -2245,7 +2265,7 @@ describe('plugin-meetings', () => {
|
|
|
2245
2265
|
meeting.passwordStatus = PASSWORD_STATUS.REQUIRED;
|
|
2246
2266
|
|
|
2247
2267
|
await meeting.fetchMeetingInfo({
|
|
2248
|
-
|
|
2268
|
+
password: 'aaa'
|
|
2249
2269
|
});
|
|
2250
2270
|
|
|
2251
2271
|
assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, 'aaa', null);
|
|
@@ -2257,6 +2277,8 @@ describe('plugin-meetings', () => {
|
|
|
2257
2277
|
});
|
|
2258
2278
|
|
|
2259
2279
|
it('refreshes captcha when captcha was required and we received 403 error code', async () => {
|
|
2280
|
+
meeting.destination = FAKE_DESTINATION;
|
|
2281
|
+
meeting.destinationType = FAKE_TYPE;
|
|
2260
2282
|
const refreshedCaptcha = {
|
|
2261
2283
|
captchaID: FAKE_WBXAPPAPI_CAPTCHA_INFO.captchaID,
|
|
2262
2284
|
verificationImageURL: FAKE_WBXAPPAPI_CAPTCHA_INFO.verificationImageURL,
|
|
@@ -2273,9 +2295,11 @@ describe('plugin-meetings', () => {
|
|
|
2273
2295
|
));
|
|
2274
2296
|
meeting.passwordStatus = PASSWORD_STATUS.REQUIRED;
|
|
2275
2297
|
meeting.requiredCaptcha = FAKE_SDK_CAPTCHA_INFO;
|
|
2298
|
+
meeting.destination = FAKE_DESTINATION;
|
|
2299
|
+
meeting.destinationType = FAKE_TYPE;
|
|
2276
2300
|
|
|
2277
2301
|
await assert.isRejected(meeting.fetchMeetingInfo({
|
|
2278
|
-
|
|
2302
|
+
password: 'aaa', captchaCode: 'bbb'
|
|
2279
2303
|
}));
|
|
2280
2304
|
|
|
2281
2305
|
assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, 'aaa', {code: 'bbb', id: FAKE_CAPTCHA_ID});
|
|
@@ -2319,7 +2343,7 @@ describe('plugin-meetings', () => {
|
|
|
2319
2343
|
};
|
|
2320
2344
|
|
|
2321
2345
|
await assert.isRejected(meeting.fetchMeetingInfo({
|
|
2322
|
-
|
|
2346
|
+
password: ''
|
|
2323
2347
|
}), CaptchaError);
|
|
2324
2348
|
|
|
2325
2349
|
assert.deepEqual(meeting.requiredCaptcha, FAKE_SDK_CAPTCHA_INFO);
|
|
@@ -2352,6 +2376,10 @@ describe('plugin-meetings', () => {
|
|
|
2352
2376
|
assert.equal(result.isPasswordValid, true);
|
|
2353
2377
|
assert.equal(result.requiredCaptcha, null);
|
|
2354
2378
|
assert.equal(result.failureReason, MEETING_INFO_FAILURE_REASON.NONE);
|
|
2379
|
+
assert.calledWith(meeting.fetchMeetingInfo, {
|
|
2380
|
+
password: 'password',
|
|
2381
|
+
captchaCode: 'captcha id',
|
|
2382
|
+
});
|
|
2355
2383
|
});
|
|
2356
2384
|
it('handles PasswordError returned by fetchMeetingInfo', async () => {
|
|
2357
2385
|
meeting.fetchMeetingInfo = sinon.stub().callsFake(() => {
|
|
@@ -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
|
});
|