@webex/plugin-meetings 3.8.0-next.5 → 3.8.0-next.6
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/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/meeting/locusMediaRequest.js +21 -5
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/webinar/index.js +1 -1
- package/package.json +3 -3
- package/src/meeting/locusMediaRequest.ts +27 -4
- package/test/unit/spec/meeting/index.js +122 -104
- package/test/unit/spec/meeting/locusMediaRequest.ts +96 -58
package/dist/breakouts/index.js
CHANGED
@@ -1046,7 +1046,7 @@ var Breakouts = _webexCore.WebexPlugin.extend({
|
|
1046
1046
|
this.trigger(_constants.BREAKOUTS.EVENTS.ASK_RETURN_TO_MAIN);
|
1047
1047
|
}
|
1048
1048
|
},
|
1049
|
-
version: "3.8.0-next.
|
1049
|
+
version: "3.8.0-next.6"
|
1050
1050
|
});
|
1051
1051
|
var _default = exports.default = Breakouts;
|
1052
1052
|
//# sourceMappingURL=index.js.map
|
@@ -373,7 +373,7 @@ var SimultaneousInterpretation = _webexCore.WebexPlugin.extend({
|
|
373
373
|
throw error;
|
374
374
|
});
|
375
375
|
},
|
376
|
-
version: "3.8.0-next.
|
376
|
+
version: "3.8.0-next.6"
|
377
377
|
});
|
378
378
|
var _default = exports.default = SimultaneousInterpretation;
|
379
379
|
//# sourceMappingURL=index.js.map
|
@@ -19,6 +19,7 @@ var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpe
|
|
19
19
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
|
20
20
|
var _lodash = require("lodash");
|
21
21
|
var _common = require("@webex/common");
|
22
|
+
var _events = require("events");
|
22
23
|
var _webexCore = require("@webex/webex-core");
|
23
24
|
var _constants = require("../constants");
|
24
25
|
var _loggerProxy = _interopRequireDefault(require("../common/logs/logger-proxy"));
|
@@ -236,13 +237,18 @@ var LocusMediaRequest = exports.LocusMediaRequest = /*#__PURE__*/function (_Webe
|
|
236
237
|
if (isRequestAffectingConfluenceState(request) && this.confluenceState === 'not created') {
|
237
238
|
this.confluenceState = 'creation in progress';
|
238
239
|
}
|
239
|
-
|
240
|
-
|
241
|
-
|
240
|
+
var upload = new _events.EventEmitter();
|
241
|
+
var download = new _events.EventEmitter();
|
242
|
+
var options = {
|
242
243
|
method: _constants.HTTP_VERBS.PUT,
|
243
244
|
uri: uri,
|
244
|
-
body: body
|
245
|
-
|
245
|
+
body: body,
|
246
|
+
upload: upload,
|
247
|
+
download: download
|
248
|
+
};
|
249
|
+
|
250
|
+
// @ts-ignore
|
251
|
+
var promise = this.request(options).then(function (result) {
|
246
252
|
if (isRequestAffectingConfluenceState(request)) {
|
247
253
|
_this4.confluenceState = 'created';
|
248
254
|
}
|
@@ -272,6 +278,16 @@ var LocusMediaRequest = exports.LocusMediaRequest = /*#__PURE__*/function (_Webe
|
|
272
278
|
}
|
273
279
|
throw e;
|
274
280
|
});
|
281
|
+
if (request.type === 'RoapMessage') {
|
282
|
+
var setupProgressListener = function setupProgressListener(direction, eventEmitter) {
|
283
|
+
eventEmitter.on('progress', function (progressEvent) {
|
284
|
+
_loggerProxy.default.logger.info("".concat(request.type, ": ").concat(direction, " Progress, Timestamp: ").concat(progressEvent.timeStamp, ", Progress: ").concat(progressEvent.loaded, "/").concat(progressEvent.total));
|
285
|
+
});
|
286
|
+
};
|
287
|
+
setupProgressListener('Upload', options.upload);
|
288
|
+
setupProgressListener('Download', options.download);
|
289
|
+
}
|
290
|
+
return promise;
|
275
291
|
}
|
276
292
|
|
277
293
|
/**
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"names":["_lodash","require","_common","_webexCore","_constants","_loggerProxy","_interopRequireDefault","_createSuper","Derived","hasNativeReflectConstruct","_isNativeReflectConstruct","_createSuperInternal","Super","_getPrototypeOf2","default","result","NewTarget","constructor","_Reflect$construct","arguments","apply","_possibleConstructorReturn2","Reflect","sham","Proxy","Boolean","prototype","valueOf","call","e","InternalRequestInfo","request","pendingPromise","sendRequestFn","_classCallCheck2","_defineProperty2","pendingPromises","_createClass2","key","value","getPendingPromises","addPendingPromises","_this$pendingPromises","push","_toConsumableArray2","execute","_this","then","forEach","d","resolve","catch","reject","isRequestAffectingConfluenceState","type","roapMessage","messageType","ROAP","ROAP_TYPES","OFFER","LocusMediaRequest","exports","_WebexPlugin","_inherits2","_super","config","options","_this2","_assertThisInitialized2","isRequestInProgress","queuedRequests","confluenceState","addToQueue","info","length","filter","r","executeNextQueuedRequest","_this3","nextRequest","shift","getLatestMuteState","audioMuted","latestAudioMuted","undefined","videoMuted","latestVideoMuted","sendHttpRequest","_this4","uri","concat","selfUrl","MEDIA","_this$getLatestMuteSt","body","device","correlationId","localMedias","respOnlySdp","usingResource","reachability","clientMediaPreferences","webex","internal","newMetrics","submitClientEvent","name","meetingId","sequence","localSdp","_stringify","mediaId","LoggerProxy","logger","method","HTTP_VERBS","PUT","rawError","send","_this5","_request$muteOptions","muteOptions","_promise","Defer","newRequest","bind","defer","promise","isConfluenceCreated","downgradeFromMultistreamToTranscoded","preferTranscoding","WebexPlugin"],"sources":["locusMediaRequest.ts"],"sourcesContent":["/* eslint-disable valid-jsdoc */\nimport {defer} from 'lodash';\nimport {Defer} from '@webex/common';\nimport {WebexPlugin} from '@webex/webex-core';\nimport {MEDIA, HTTP_VERBS, ROAP} from '../constants';\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport {ClientMediaPreferences} from '../reachability/reachability.types';\n\nexport type MediaRequestType = 'RoapMessage' | 'LocalMute';\nexport type RequestResult = any;\n\nexport type RoapRequest = {\n type: 'RoapMessage';\n selfUrl: string;\n mediaId: string;\n roapMessage: any;\n reachability: any;\n clientMediaPreferences: ClientMediaPreferences;\n sequence?: any;\n};\n\nexport type LocalMuteRequest = {\n type: 'LocalMute';\n selfUrl: string;\n mediaId: string;\n sequence?: any;\n muteOptions: {\n audioMuted?: boolean;\n videoMuted?: boolean;\n };\n};\n\nexport type Request = RoapRequest | LocalMuteRequest;\n\n/** Class representing a single /media request being sent to Locus */\nclass InternalRequestInfo {\n public readonly request: Request;\n private pendingPromises: Defer[];\n private sendRequestFn: (request: Request) => Promise<RequestResult>;\n\n /** Constructor */\n constructor(\n request: Request,\n pendingPromise: Defer,\n sendRequestFn: (request: Request) => Promise<RequestResult>\n ) {\n this.request = request;\n this.pendingPromises = [pendingPromise];\n this.sendRequestFn = sendRequestFn;\n }\n\n /**\n * Returns the list of pending promises associated with this request\n */\n public getPendingPromises() {\n return this.pendingPromises;\n }\n\n /**\n * Adds promises to the list of pending promises associated with this request\n */\n public addPendingPromises(pendingPromises: Defer[]) {\n this.pendingPromises.push(...pendingPromises);\n }\n\n /**\n * Executes the request. Returned promise is resolved once the request\n * is completed (no matter if it succeeded or failed).\n */\n public execute(): Promise<void> {\n return this.sendRequestFn(this.request)\n .then((result) => {\n // resolve all the pending promises associated with this request\n this.pendingPromises.forEach((d) => d.resolve(result));\n })\n .catch((e) => {\n // reject all the pending promises associated with this request\n this.pendingPromises.forEach((d) => d.reject(e));\n });\n }\n}\n\nexport type Config = {\n device: {\n url: string;\n deviceType: string;\n countryCode?: string;\n regionCode?: string;\n };\n correlationId: string;\n meetingId: string;\n preferTranscoding: boolean;\n};\n\n/**\n * Returns true if the request is triggering confluence creation in the server\n */\nfunction isRequestAffectingConfluenceState(request: Request): boolean {\n return (\n request.type === 'RoapMessage' && request.roapMessage.messageType === ROAP.ROAP_TYPES.OFFER\n );\n}\n\n/**\n * This class manages all /media API requests to Locus. Every call to that\n * Locus API has to go through this class.\n */\nexport class LocusMediaRequest extends WebexPlugin {\n private config: Config;\n private latestAudioMuted?: boolean;\n private latestVideoMuted?: boolean;\n private isRequestInProgress: boolean;\n private queuedRequests: InternalRequestInfo[];\n private confluenceState: 'not created' | 'creation in progress' | 'created';\n /**\n * Constructor\n */\n constructor(config: Config, options: any) {\n super({}, options);\n this.isRequestInProgress = false;\n this.queuedRequests = [];\n this.config = config;\n this.confluenceState = 'not created';\n }\n\n /**\n * Add a request to the internal queue.\n */\n private addToQueue(info: InternalRequestInfo) {\n if (info.request.type === 'LocalMute' && this.queuedRequests.length > 0) {\n // We don't need additional local mute requests in the queue.\n // We only need at most 1 local mute or 1 roap request, because\n // roap requests also include mute state, so whatever request\n // is sent out, it will send the latest local mute state.\n // We only need to store the pendingPromises so that they get resolved\n // when the roap request is sent out.\n this.queuedRequests[0].addPendingPromises(info.getPendingPromises());\n\n return;\n }\n\n if (info.request.type === 'RoapMessage' && this.queuedRequests.length > 0) {\n // remove any LocalMute requests from the queue, because this Roap message\n // will also update the mute status in Locus, so they are redundant\n this.queuedRequests = this.queuedRequests.filter((r) => {\n if (r.request.type === 'LocalMute') {\n // we need to keep the pending promises from the local mute request\n // that we're removing from the queue\n info.addPendingPromises(r.getPendingPromises());\n\n return false;\n }\n\n return true;\n });\n }\n\n this.queuedRequests.push(info);\n }\n\n /**\n * Takes the next request from the queue and executes it. Once that\n * request is completed, the next one will be taken from the queue\n * and executed and this is repeated until the queue is empty.\n */\n private executeNextQueuedRequest(): void {\n if (this.isRequestInProgress) {\n return;\n }\n\n const nextRequest = this.queuedRequests.shift();\n\n if (nextRequest) {\n this.isRequestInProgress = true;\n nextRequest.execute().then(() => {\n this.isRequestInProgress = false;\n this.executeNextQueuedRequest();\n });\n }\n }\n\n /**\n * Returns latest requested audio and video mute values. If they have never been\n * requested, we assume audio/video to be muted.\n */\n private getLatestMuteState() {\n const audioMuted = this.latestAudioMuted !== undefined ? this.latestAudioMuted : true;\n const videoMuted = this.latestVideoMuted !== undefined ? this.latestVideoMuted : true;\n\n return {audioMuted, videoMuted};\n }\n\n /**\n * Prepares the uri and body for the media request to be sent to Locus\n */\n private sendHttpRequest(request: Request) {\n const uri = `${request.selfUrl}/${MEDIA}`;\n\n const {audioMuted, videoMuted} = this.getLatestMuteState();\n\n // first setup things common to all requests\n const body: any = {\n device: this.config.device,\n correlationId: this.config.correlationId,\n };\n\n const localMedias: any = {\n audioMuted,\n videoMuted,\n };\n\n // now add things specific to request type\n switch (request.type) {\n case 'LocalMute':\n body.respOnlySdp = true;\n body.usingResource = null;\n break;\n\n case 'RoapMessage':\n localMedias.roapMessage = request.roapMessage;\n localMedias.reachability = request.reachability;\n body.clientMediaPreferences = request.clientMediaPreferences;\n\n // @ts-ignore\n this.webex.internal.newMetrics.submitClientEvent({\n name: 'client.locus.media.request',\n options: {\n meetingId: this.config.meetingId,\n },\n });\n break;\n }\n\n if (request.sequence) {\n body.sequence = request.sequence;\n }\n\n body.localMedias = [\n {\n localSdp: JSON.stringify(localMedias), // this part must be JSON stringified, Locus requires this\n mediaId: request.mediaId,\n },\n ];\n\n LoggerProxy.logger.info(\n `Meeting:LocusMediaRequest#sendHttpRequest --> ${request.type} audioMuted=${audioMuted} videoMuted=${videoMuted}`\n );\n\n if (isRequestAffectingConfluenceState(request) && this.confluenceState === 'not created') {\n this.confluenceState = 'creation in progress';\n }\n\n // @ts-ignore\n return this.request({\n method: HTTP_VERBS.PUT,\n uri,\n body,\n })\n .then((result) => {\n if (isRequestAffectingConfluenceState(request)) {\n this.confluenceState = 'created';\n }\n\n if (request.type === 'RoapMessage') {\n // @ts-ignore\n this.webex.internal.newMetrics.submitClientEvent({\n name: 'client.locus.media.response',\n options: {\n meetingId: this.config.meetingId,\n },\n });\n }\n\n return result;\n })\n .catch((e) => {\n if (\n isRequestAffectingConfluenceState(request) &&\n this.confluenceState === 'creation in progress'\n ) {\n this.confluenceState = 'not created';\n }\n\n if (request.type === 'RoapMessage') {\n // @ts-ignore\n this.webex.internal.newMetrics.submitClientEvent({\n name: 'client.locus.media.response',\n options: {\n meetingId: this.config.meetingId,\n rawError: e,\n },\n });\n }\n\n throw e;\n });\n }\n\n /**\n * Sends a media request to Locus\n */\n public send(request: Request): Promise<RequestResult> {\n if (request.type === 'LocalMute') {\n const {audioMuted, videoMuted} = request.muteOptions;\n\n if (audioMuted !== undefined) {\n this.latestAudioMuted = audioMuted;\n }\n if (videoMuted !== undefined) {\n this.latestVideoMuted = videoMuted;\n }\n\n if (this.confluenceState === 'not created') {\n // if there is no confluence, there is no point sending out local mute request\n // as it will fail so we just store the latest audio/video muted values\n // and resolve immediately, so that higher layer (MuteState class) doesn't get blocked\n // and can call us again if user mutes/unmutes again before confluence is created\n LoggerProxy.logger.info(\n 'Meeting:LocusMediaRequest#send --> called with LocalMute request before confluence creation'\n );\n\n return Promise.resolve({});\n }\n }\n\n const pendingPromise = new Defer();\n\n const newRequest = new InternalRequestInfo(\n request,\n pendingPromise,\n this.sendHttpRequest.bind(this)\n );\n\n this.addToQueue(newRequest);\n\n defer(() => this.executeNextQueuedRequest());\n\n return pendingPromise.promise;\n }\n\n /** Returns true if a confluence on the server is already created */\n public isConfluenceCreated() {\n return this.confluenceState === 'created';\n }\n\n /**\n * This method needs to be called when we downgrade from multistream to transcoded connection.\n */\n public downgradeFromMultistreamToTranscoded() {\n this.config.preferTranscoding = true;\n }\n}\n"],"mappings":"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"}
|
1
|
+
{"version":3,"names":["_lodash","require","_common","_events","_webexCore","_constants","_loggerProxy","_interopRequireDefault","_createSuper","Derived","hasNativeReflectConstruct","_isNativeReflectConstruct","_createSuperInternal","Super","_getPrototypeOf2","default","result","NewTarget","constructor","_Reflect$construct","arguments","apply","_possibleConstructorReturn2","Reflect","sham","Proxy","Boolean","prototype","valueOf","call","e","InternalRequestInfo","request","pendingPromise","sendRequestFn","_classCallCheck2","_defineProperty2","pendingPromises","_createClass2","key","value","getPendingPromises","addPendingPromises","_this$pendingPromises","push","_toConsumableArray2","execute","_this","then","forEach","d","resolve","catch","reject","isRequestAffectingConfluenceState","type","roapMessage","messageType","ROAP","ROAP_TYPES","OFFER","LocusMediaRequest","exports","_WebexPlugin","_inherits2","_super","config","options","_this2","_assertThisInitialized2","isRequestInProgress","queuedRequests","confluenceState","addToQueue","info","length","filter","r","executeNextQueuedRequest","_this3","nextRequest","shift","getLatestMuteState","audioMuted","latestAudioMuted","undefined","videoMuted","latestVideoMuted","sendHttpRequest","_this4","uri","concat","selfUrl","MEDIA","_this$getLatestMuteSt","body","device","correlationId","localMedias","respOnlySdp","usingResource","reachability","clientMediaPreferences","webex","internal","newMetrics","submitClientEvent","name","meetingId","sequence","localSdp","_stringify","mediaId","LoggerProxy","logger","upload","EventEmitter","download","method","HTTP_VERBS","PUT","promise","rawError","setupProgressListener","direction","eventEmitter","on","progressEvent","timeStamp","loaded","total","send","_this5","_request$muteOptions","muteOptions","_promise","Defer","newRequest","bind","defer","isConfluenceCreated","downgradeFromMultistreamToTranscoded","preferTranscoding","WebexPlugin"],"sources":["locusMediaRequest.ts"],"sourcesContent":["/* eslint-disable valid-jsdoc */\nimport {defer} from 'lodash';\nimport {Defer, transferEvents} from '@webex/common';\nimport {EventEmitter} from 'events';\nimport {WebexPlugin} from '@webex/webex-core';\nimport {MEDIA, HTTP_VERBS, ROAP} from '../constants';\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport {ClientMediaPreferences} from '../reachability/reachability.types';\n\nexport type MediaRequestType = 'RoapMessage' | 'LocalMute';\nexport type RequestResult = any;\n\nexport type RoapRequest = {\n type: 'RoapMessage';\n selfUrl: string;\n mediaId: string;\n roapMessage: any;\n reachability: any;\n clientMediaPreferences: ClientMediaPreferences;\n sequence?: any;\n};\n\nexport type LocalMuteRequest = {\n type: 'LocalMute';\n selfUrl: string;\n mediaId: string;\n sequence?: any;\n muteOptions: {\n audioMuted?: boolean;\n videoMuted?: boolean;\n };\n};\n\nexport type Request = RoapRequest | LocalMuteRequest;\n\n/** Class representing a single /media request being sent to Locus */\nclass InternalRequestInfo {\n public readonly request: Request;\n private pendingPromises: Defer[];\n private sendRequestFn: (request: Request) => Promise<RequestResult>;\n\n /** Constructor */\n constructor(\n request: Request,\n pendingPromise: Defer,\n sendRequestFn: (request: Request) => Promise<RequestResult>\n ) {\n this.request = request;\n this.pendingPromises = [pendingPromise];\n this.sendRequestFn = sendRequestFn;\n }\n\n /**\n * Returns the list of pending promises associated with this request\n */\n public getPendingPromises() {\n return this.pendingPromises;\n }\n\n /**\n * Adds promises to the list of pending promises associated with this request\n */\n public addPendingPromises(pendingPromises: Defer[]) {\n this.pendingPromises.push(...pendingPromises);\n }\n\n /**\n * Executes the request. Returned promise is resolved once the request\n * is completed (no matter if it succeeded or failed).\n */\n public execute(): Promise<void> {\n return this.sendRequestFn(this.request)\n .then((result) => {\n // resolve all the pending promises associated with this request\n this.pendingPromises.forEach((d) => d.resolve(result));\n })\n .catch((e) => {\n // reject all the pending promises associated with this request\n this.pendingPromises.forEach((d) => d.reject(e));\n });\n }\n}\n\nexport type Config = {\n device: {\n url: string;\n deviceType: string;\n countryCode?: string;\n regionCode?: string;\n };\n correlationId: string;\n meetingId: string;\n preferTranscoding: boolean;\n};\n\n/**\n * Returns true if the request is triggering confluence creation in the server\n */\nfunction isRequestAffectingConfluenceState(request: Request): boolean {\n return (\n request.type === 'RoapMessage' && request.roapMessage.messageType === ROAP.ROAP_TYPES.OFFER\n );\n}\n\n/**\n * This class manages all /media API requests to Locus. Every call to that\n * Locus API has to go through this class.\n */\nexport class LocusMediaRequest extends WebexPlugin {\n private config: Config;\n private latestAudioMuted?: boolean;\n private latestVideoMuted?: boolean;\n private isRequestInProgress: boolean;\n private queuedRequests: InternalRequestInfo[];\n private confluenceState: 'not created' | 'creation in progress' | 'created';\n /**\n * Constructor\n */\n constructor(config: Config, options: any) {\n super({}, options);\n this.isRequestInProgress = false;\n this.queuedRequests = [];\n this.config = config;\n this.confluenceState = 'not created';\n }\n\n /**\n * Add a request to the internal queue.\n */\n private addToQueue(info: InternalRequestInfo) {\n if (info.request.type === 'LocalMute' && this.queuedRequests.length > 0) {\n // We don't need additional local mute requests in the queue.\n // We only need at most 1 local mute or 1 roap request, because\n // roap requests also include mute state, so whatever request\n // is sent out, it will send the latest local mute state.\n // We only need to store the pendingPromises so that they get resolved\n // when the roap request is sent out.\n this.queuedRequests[0].addPendingPromises(info.getPendingPromises());\n\n return;\n }\n\n if (info.request.type === 'RoapMessage' && this.queuedRequests.length > 0) {\n // remove any LocalMute requests from the queue, because this Roap message\n // will also update the mute status in Locus, so they are redundant\n this.queuedRequests = this.queuedRequests.filter((r) => {\n if (r.request.type === 'LocalMute') {\n // we need to keep the pending promises from the local mute request\n // that we're removing from the queue\n info.addPendingPromises(r.getPendingPromises());\n\n return false;\n }\n\n return true;\n });\n }\n\n this.queuedRequests.push(info);\n }\n\n /**\n * Takes the next request from the queue and executes it. Once that\n * request is completed, the next one will be taken from the queue\n * and executed and this is repeated until the queue is empty.\n */\n private executeNextQueuedRequest(): void {\n if (this.isRequestInProgress) {\n return;\n }\n\n const nextRequest = this.queuedRequests.shift();\n\n if (nextRequest) {\n this.isRequestInProgress = true;\n nextRequest.execute().then(() => {\n this.isRequestInProgress = false;\n this.executeNextQueuedRequest();\n });\n }\n }\n\n /**\n * Returns latest requested audio and video mute values. If they have never been\n * requested, we assume audio/video to be muted.\n */\n private getLatestMuteState() {\n const audioMuted = this.latestAudioMuted !== undefined ? this.latestAudioMuted : true;\n const videoMuted = this.latestVideoMuted !== undefined ? this.latestVideoMuted : true;\n\n return {audioMuted, videoMuted};\n }\n\n /**\n * Prepares the uri and body for the media request to be sent to Locus\n */\n private sendHttpRequest(request: Request) {\n const uri = `${request.selfUrl}/${MEDIA}`;\n\n const {audioMuted, videoMuted} = this.getLatestMuteState();\n\n // first setup things common to all requests\n const body: any = {\n device: this.config.device,\n correlationId: this.config.correlationId,\n };\n\n const localMedias: any = {\n audioMuted,\n videoMuted,\n };\n\n // now add things specific to request type\n switch (request.type) {\n case 'LocalMute':\n body.respOnlySdp = true;\n body.usingResource = null;\n break;\n\n case 'RoapMessage':\n localMedias.roapMessage = request.roapMessage;\n localMedias.reachability = request.reachability;\n body.clientMediaPreferences = request.clientMediaPreferences;\n\n // @ts-ignore\n this.webex.internal.newMetrics.submitClientEvent({\n name: 'client.locus.media.request',\n options: {\n meetingId: this.config.meetingId,\n },\n });\n break;\n }\n\n if (request.sequence) {\n body.sequence = request.sequence;\n }\n\n body.localMedias = [\n {\n localSdp: JSON.stringify(localMedias), // this part must be JSON stringified, Locus requires this\n mediaId: request.mediaId,\n },\n ];\n\n LoggerProxy.logger.info(\n `Meeting:LocusMediaRequest#sendHttpRequest --> ${request.type} audioMuted=${audioMuted} videoMuted=${videoMuted}`\n );\n\n if (isRequestAffectingConfluenceState(request) && this.confluenceState === 'not created') {\n this.confluenceState = 'creation in progress';\n }\n\n const upload = new EventEmitter();\n const download = new EventEmitter();\n\n const options = {\n method: HTTP_VERBS.PUT,\n uri,\n body,\n upload,\n download,\n };\n\n // @ts-ignore\n const promise = this.request(options)\n .then((result) => {\n if (isRequestAffectingConfluenceState(request)) {\n this.confluenceState = 'created';\n }\n\n if (request.type === 'RoapMessage') {\n // @ts-ignore\n this.webex.internal.newMetrics.submitClientEvent({\n name: 'client.locus.media.response',\n options: {\n meetingId: this.config.meetingId,\n },\n });\n }\n\n return result;\n })\n .catch((e) => {\n if (\n isRequestAffectingConfluenceState(request) &&\n this.confluenceState === 'creation in progress'\n ) {\n this.confluenceState = 'not created';\n }\n\n if (request.type === 'RoapMessage') {\n // @ts-ignore\n this.webex.internal.newMetrics.submitClientEvent({\n name: 'client.locus.media.response',\n options: {\n meetingId: this.config.meetingId,\n rawError: e,\n },\n });\n }\n\n throw e;\n });\n\n if (request.type === 'RoapMessage') {\n const setupProgressListener = (direction: string, eventEmitter: EventEmitter) => {\n eventEmitter.on('progress', (progressEvent: ProgressEvent) => {\n LoggerProxy.logger.info(\n `${request.type}: ${direction} Progress, Timestamp: ${progressEvent.timeStamp}, Progress: ${progressEvent.loaded}/${progressEvent.total}`\n );\n });\n };\n\n setupProgressListener('Upload', options.upload);\n setupProgressListener('Download', options.download);\n }\n\n return promise;\n }\n\n /**\n * Sends a media request to Locus\n */\n public send(request: Request): Promise<RequestResult> {\n if (request.type === 'LocalMute') {\n const {audioMuted, videoMuted} = request.muteOptions;\n\n if (audioMuted !== undefined) {\n this.latestAudioMuted = audioMuted;\n }\n if (videoMuted !== undefined) {\n this.latestVideoMuted = videoMuted;\n }\n\n if (this.confluenceState === 'not created') {\n // if there is no confluence, there is no point sending out local mute request\n // as it will fail so we just store the latest audio/video muted values\n // and resolve immediately, so that higher layer (MuteState class) doesn't get blocked\n // and can call us again if user mutes/unmutes again before confluence is created\n LoggerProxy.logger.info(\n 'Meeting:LocusMediaRequest#send --> called with LocalMute request before confluence creation'\n );\n\n return Promise.resolve({});\n }\n }\n\n const pendingPromise = new Defer();\n\n const newRequest = new InternalRequestInfo(\n request,\n pendingPromise,\n this.sendHttpRequest.bind(this)\n );\n\n this.addToQueue(newRequest);\n\n defer(() => this.executeNextQueuedRequest());\n\n return pendingPromise.promise;\n }\n\n /** Returns true if a confluence on the server is already created */\n public isConfluenceCreated() {\n return this.confluenceState === 'created';\n }\n\n /**\n * This method needs to be called when we downgrade from multistream to transcoded connection.\n */\n public downgradeFromMultistreamToTranscoded() {\n this.config.preferTranscoding = true;\n }\n}\n"],"mappings":"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"}
|
package/dist/webinar/index.js
CHANGED
package/package.json
CHANGED
@@ -43,7 +43,7 @@
|
|
43
43
|
"@webex/eslint-config-legacy": "0.0.0",
|
44
44
|
"@webex/jest-config-legacy": "0.0.0",
|
45
45
|
"@webex/legacy-tools": "0.0.0",
|
46
|
-
"@webex/plugin-meetings": "3.8.0-next.
|
46
|
+
"@webex/plugin-meetings": "3.8.0-next.6",
|
47
47
|
"@webex/plugin-rooms": "3.8.0-next.1",
|
48
48
|
"@webex/test-helper-chai": "3.8.0-next.1",
|
49
49
|
"@webex/test-helper-mocha": "3.8.0-next.1",
|
@@ -71,7 +71,7 @@
|
|
71
71
|
"@webex/internal-plugin-metrics": "3.8.0-next.1",
|
72
72
|
"@webex/internal-plugin-support": "3.8.0-next.1",
|
73
73
|
"@webex/internal-plugin-user": "3.8.0-next.1",
|
74
|
-
"@webex/internal-plugin-voicea": "3.8.0-next.
|
74
|
+
"@webex/internal-plugin-voicea": "3.8.0-next.6",
|
75
75
|
"@webex/media-helpers": "3.8.0-next.1",
|
76
76
|
"@webex/plugin-people": "3.8.0-next.1",
|
77
77
|
"@webex/plugin-rooms": "3.8.0-next.1",
|
@@ -92,5 +92,5 @@
|
|
92
92
|
"//": [
|
93
93
|
"TODO: upgrade jwt-decode when moving to node 18"
|
94
94
|
],
|
95
|
-
"version": "3.8.0-next.
|
95
|
+
"version": "3.8.0-next.6"
|
96
96
|
}
|
@@ -1,6 +1,7 @@
|
|
1
1
|
/* eslint-disable valid-jsdoc */
|
2
2
|
import {defer} from 'lodash';
|
3
|
-
import {Defer} from '@webex/common';
|
3
|
+
import {Defer, transferEvents} from '@webex/common';
|
4
|
+
import {EventEmitter} from 'events';
|
4
5
|
import {WebexPlugin} from '@webex/webex-core';
|
5
6
|
import {MEDIA, HTTP_VERBS, ROAP} from '../constants';
|
6
7
|
import LoggerProxy from '../common/logs/logger-proxy';
|
@@ -250,12 +251,19 @@ export class LocusMediaRequest extends WebexPlugin {
|
|
250
251
|
this.confluenceState = 'creation in progress';
|
251
252
|
}
|
252
253
|
|
253
|
-
|
254
|
-
|
254
|
+
const upload = new EventEmitter();
|
255
|
+
const download = new EventEmitter();
|
256
|
+
|
257
|
+
const options = {
|
255
258
|
method: HTTP_VERBS.PUT,
|
256
259
|
uri,
|
257
260
|
body,
|
258
|
-
|
261
|
+
upload,
|
262
|
+
download,
|
263
|
+
};
|
264
|
+
|
265
|
+
// @ts-ignore
|
266
|
+
const promise = this.request(options)
|
259
267
|
.then((result) => {
|
260
268
|
if (isRequestAffectingConfluenceState(request)) {
|
261
269
|
this.confluenceState = 'created';
|
@@ -294,6 +302,21 @@ export class LocusMediaRequest extends WebexPlugin {
|
|
294
302
|
|
295
303
|
throw e;
|
296
304
|
});
|
305
|
+
|
306
|
+
if (request.type === 'RoapMessage') {
|
307
|
+
const setupProgressListener = (direction: string, eventEmitter: EventEmitter) => {
|
308
|
+
eventEmitter.on('progress', (progressEvent: ProgressEvent) => {
|
309
|
+
LoggerProxy.logger.info(
|
310
|
+
`${request.type}: ${direction} Progress, Timestamp: ${progressEvent.timeStamp}, Progress: ${progressEvent.loaded}/${progressEvent.total}`
|
311
|
+
);
|
312
|
+
});
|
313
|
+
};
|
314
|
+
|
315
|
+
setupProgressListener('Upload', options.upload);
|
316
|
+
setupProgressListener('Download', options.download);
|
317
|
+
}
|
318
|
+
|
319
|
+
return promise;
|
297
320
|
}
|
298
321
|
|
299
322
|
/**
|
@@ -93,13 +93,14 @@ import CaptchaError from '../../../../src/common/errors/captcha-error';
|
|
93
93
|
import PermissionError from '../../../../src/common/errors/permission';
|
94
94
|
import JoinWebinarError from '../../../../src/common/errors/join-webinar-error';
|
95
95
|
import IntentToJoinError from '../../../../src/common/errors/intent-to-join';
|
96
|
-
import MultistreamNotSupportedError from '../../../../src/common/errors/multistream-not-supported-error'
|
96
|
+
import MultistreamNotSupportedError from '../../../../src/common/errors/multistream-not-supported-error';
|
97
97
|
import testUtils from '../../../utils/testUtils';
|
98
98
|
import {
|
99
99
|
MeetingInfoV2CaptchaError,
|
100
100
|
MeetingInfoV2PasswordError,
|
101
101
|
MeetingInfoV2PolicyError,
|
102
|
-
MeetingInfoV2JoinWebinarError,
|
102
|
+
MeetingInfoV2JoinWebinarError,
|
103
|
+
MeetingInfoV2JoinForbiddenError,
|
103
104
|
} from '../../../../src/meeting-info/meeting-info-v2';
|
104
105
|
import {
|
105
106
|
DTLS_HANDSHAKE_FAILED_CLIENT_CODE,
|
@@ -115,7 +116,8 @@ import MeetingCollection from '@webex/plugin-meetings/src/meetings/collection';
|
|
115
116
|
|
116
117
|
import {EVENT_TRIGGERS as VOICEAEVENTS} from '@webex/internal-plugin-voicea';
|
117
118
|
import { createBrbState } from '@webex/plugin-meetings/src/meeting/brbState';
|
118
|
-
import JoinForbiddenError
|
119
|
+
import JoinForbiddenError from '../../../../src/common/errors/join-forbidden-error';
|
120
|
+
import { EventEmitter } from 'stream';
|
119
121
|
|
120
122
|
describe('plugin-meetings', () => {
|
121
123
|
const logger = {
|
@@ -208,6 +210,7 @@ describe('plugin-meetings', () => {
|
|
208
210
|
let membersSpy;
|
209
211
|
let meetingRequestSpy;
|
210
212
|
let correlationId;
|
213
|
+
let uploadEvent;
|
211
214
|
|
212
215
|
beforeEach(() => {
|
213
216
|
webex = new MockWebex({
|
@@ -277,6 +280,8 @@ describe('plugin-meetings', () => {
|
|
277
280
|
test4 = `test4-${uuid.v4()}`;
|
278
281
|
testDestination = `testDestination-${uuid.v4()}`;
|
279
282
|
correlationId = uuid.v4();
|
283
|
+
uploadEvent = new EventEmitter();
|
284
|
+
uploadEvent.addListener('progress', () => {})
|
280
285
|
|
281
286
|
meeting = new Meeting(
|
282
287
|
{
|
@@ -667,7 +672,7 @@ describe('plugin-meetings', () => {
|
|
667
672
|
beforeEach(() => {
|
668
673
|
meeting.join = sinon.stub().callsFake((joinOptions) => {
|
669
674
|
meeting.isMultistream = joinOptions.enableMultistream;
|
670
|
-
return Promise.resolve(fakeJoinResult)
|
675
|
+
return Promise.resolve(fakeJoinResult);
|
671
676
|
});
|
672
677
|
addMediaInternalStub = sinon
|
673
678
|
.stub(meeting, 'addMediaInternal')
|
@@ -1070,7 +1075,11 @@ describe('plugin-meetings', () => {
|
|
1070
1075
|
mediaOptions,
|
1071
1076
|
});
|
1072
1077
|
|
1073
|
-
assert.deepEqual(result, {
|
1078
|
+
assert.deepEqual(result, {
|
1079
|
+
join: fakeJoinResult,
|
1080
|
+
media: undefined,
|
1081
|
+
multistreamEnabled: false,
|
1082
|
+
});
|
1074
1083
|
|
1075
1084
|
assert.calledOnce(meeting.join);
|
1076
1085
|
|
@@ -1174,7 +1183,10 @@ describe('plugin-meetings', () => {
|
|
1174
1183
|
type: addMediaError.name,
|
1175
1184
|
}
|
1176
1185
|
);
|
1177
|
-
assert.calledOnceWithExactly(meeting.leave, {
|
1186
|
+
assert.calledOnceWithExactly(meeting.leave, {
|
1187
|
+
resourceId: undefined,
|
1188
|
+
reason: 'joinWithMedia failure',
|
1189
|
+
});
|
1178
1190
|
});
|
1179
1191
|
});
|
1180
1192
|
|
@@ -3550,18 +3562,18 @@ describe('plugin-meetings', () => {
|
|
3550
3562
|
it('counts the number of members that are in the meeting for MEDIA_QUALITY event', async () => {
|
3551
3563
|
let fakeMembersCollection = {
|
3552
3564
|
members: {
|
3553
|
-
member1: {
|
3554
|
-
member2: {
|
3555
|
-
member3: {
|
3565
|
+
member1: {isInMeeting: true},
|
3566
|
+
member2: {isInMeeting: true},
|
3567
|
+
member3: {isInMeeting: false},
|
3556
3568
|
},
|
3557
3569
|
};
|
3558
|
-
sinon.stub(meeting, 'getMembers').returns({
|
3559
|
-
const fakeData = {
|
3570
|
+
sinon.stub(meeting, 'getMembers').returns({membersCollection: fakeMembersCollection});
|
3571
|
+
const fakeData = {intervalMetadata: {}, networkType: 'wifi'};
|
3560
3572
|
|
3561
3573
|
statsAnalyzerStub.emit(
|
3562
|
-
{
|
3574
|
+
{file: 'test', function: 'test'},
|
3563
3575
|
StatsAnalyzerEventNames.MEDIA_QUALITY,
|
3564
|
-
{
|
3576
|
+
{data: fakeData}
|
3565
3577
|
);
|
3566
3578
|
|
3567
3579
|
assert.calledWithMatch(webex.internal.newMetrics.submitMQE, {
|
@@ -3570,15 +3582,17 @@ describe('plugin-meetings', () => {
|
|
3570
3582
|
meetingId: meeting.id,
|
3571
3583
|
},
|
3572
3584
|
payload: {
|
3573
|
-
intervals: [
|
3585
|
+
intervals: [
|
3586
|
+
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 2)),
|
3587
|
+
],
|
3574
3588
|
},
|
3575
3589
|
});
|
3576
3590
|
fakeMembersCollection.members.member2.isInMeeting = false;
|
3577
3591
|
|
3578
3592
|
statsAnalyzerStub.emit(
|
3579
|
-
{
|
3593
|
+
{file: 'test', function: 'test'},
|
3580
3594
|
StatsAnalyzerEventNames.MEDIA_QUALITY,
|
3581
|
-
{
|
3595
|
+
{data: fakeData}
|
3582
3596
|
);
|
3583
3597
|
|
3584
3598
|
assert.calledWithMatch(webex.internal.newMetrics.submitMQE, {
|
@@ -3587,7 +3601,9 @@ describe('plugin-meetings', () => {
|
|
3587
3601
|
meetingId: meeting.id,
|
3588
3602
|
},
|
3589
3603
|
payload: {
|
3590
|
-
intervals: [
|
3604
|
+
intervals: [
|
3605
|
+
sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 1)),
|
3606
|
+
],
|
3591
3607
|
},
|
3592
3608
|
});
|
3593
3609
|
});
|
@@ -3842,7 +3858,6 @@ describe('plugin-meetings', () => {
|
|
3842
3858
|
});
|
3843
3859
|
|
3844
3860
|
describe('when in a multistream meeting', () => {
|
3845
|
-
|
3846
3861
|
beforeEach(() => {
|
3847
3862
|
meeting.isMultistream = true;
|
3848
3863
|
});
|
@@ -3853,7 +3868,7 @@ describe('plugin-meetings', () => {
|
|
3853
3868
|
await brbResult;
|
3854
3869
|
assert.exists(brbResult.then);
|
3855
3870
|
assert.calledOnce(meeting.brbState.enable);
|
3856
|
-
})
|
3871
|
+
});
|
3857
3872
|
|
3858
3873
|
it('should disable #beRightBack and return a promise', async () => {
|
3859
3874
|
const brbResult = meeting.beRightBack(false);
|
@@ -3861,7 +3876,7 @@ describe('plugin-meetings', () => {
|
|
3861
3876
|
await brbResult;
|
3862
3877
|
assert.exists(brbResult.then);
|
3863
3878
|
assert.calledOnce(meeting.brbState.enable);
|
3864
|
-
})
|
3879
|
+
});
|
3865
3880
|
|
3866
3881
|
it('should throw an error and reject the promise if setBrb fails', async () => {
|
3867
3882
|
const error = new Error('setBrb failed');
|
@@ -3874,7 +3889,7 @@ describe('plugin-meetings', () => {
|
|
3874
3889
|
assert.equal(err.message, 'setBrb failed');
|
3875
3890
|
assert.isRejected((Promise.reject()));
|
3876
3891
|
}
|
3877
|
-
})
|
3892
|
+
});
|
3878
3893
|
});
|
3879
3894
|
});
|
3880
3895
|
|
@@ -4006,7 +4021,7 @@ describe('plugin-meetings', () => {
|
|
4006
4021
|
initiateOffer: sinon.stub().resolves({}),
|
4007
4022
|
update: sinon.stub().resolves({}),
|
4008
4023
|
on: sinon.stub(),
|
4009
|
-
roapMessageReceived: sinon.stub()
|
4024
|
+
roapMessageReceived: sinon.stub(),
|
4010
4025
|
};
|
4011
4026
|
|
4012
4027
|
fakeMultistreamRoapMediaConnection = {
|
@@ -4035,7 +4050,7 @@ describe('plugin-meetings', () => {
|
|
4035
4050
|
|
4036
4051
|
locusMediaRequestStub = sinon
|
4037
4052
|
.stub(WebexPlugin.prototype, 'request')
|
4038
|
-
.resolves({body: {locus: {fullState: {}}}});
|
4053
|
+
.resolves({body: {locus: {fullState: {}}}, upload: sinon.match.instanceOf(EventEmitter), download: sinon.match.instanceOf(EventEmitter)});
|
4039
4054
|
|
4040
4055
|
// setup some things and mocks so that the call to join() works
|
4041
4056
|
// (we need to call join() because it creates the LocusMediaRequest instance
|
@@ -4144,6 +4159,8 @@ describe('plugin-meetings', () => {
|
|
4144
4159
|
id: 'fake clientMediaPreferences',
|
4145
4160
|
},
|
4146
4161
|
},
|
4162
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
4163
|
+
download: sinon.match.instanceOf(EventEmitter),
|
4147
4164
|
});
|
4148
4165
|
};
|
4149
4166
|
|
@@ -4171,6 +4188,8 @@ describe('plugin-meetings', () => {
|
|
4171
4188
|
},
|
4172
4189
|
],
|
4173
4190
|
},
|
4191
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
4192
|
+
download: sinon.match.instanceOf(EventEmitter),
|
4174
4193
|
});
|
4175
4194
|
};
|
4176
4195
|
|
@@ -4195,6 +4214,8 @@ describe('plugin-meetings', () => {
|
|
4195
4214
|
respOnlySdp: true,
|
4196
4215
|
usingResource: null,
|
4197
4216
|
},
|
4217
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
4218
|
+
download: sinon.match.instanceOf(EventEmitter),
|
4198
4219
|
});
|
4199
4220
|
};
|
4200
4221
|
|
@@ -6337,7 +6358,10 @@ describe('plugin-meetings', () => {
|
|
6337
6358
|
.throws(new MeetingInfoV2JoinForbiddenError(403003, FAKE_MEETING_INFO)),
|
6338
6359
|
};
|
6339
6360
|
|
6340
|
-
await assert.isRejected(
|
6361
|
+
await assert.isRejected(
|
6362
|
+
meeting.fetchMeetingInfo({sendCAevents: true}),
|
6363
|
+
JoinForbiddenError
|
6364
|
+
);
|
6341
6365
|
|
6342
6366
|
assert.calledWith(
|
6343
6367
|
meeting.attrs.meetingInfoProvider.fetchMeetingInfo,
|
@@ -6353,10 +6377,7 @@ describe('plugin-meetings', () => {
|
|
6353
6377
|
|
6354
6378
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
6355
6379
|
assert.equal(meeting.meetingInfoFailureCode, 403003);
|
6356
|
-
assert.equal(
|
6357
|
-
meeting.meetingInfoFailureReason,
|
6358
|
-
MEETING_INFO_FAILURE_REASON.NOT_REACH_JBH
|
6359
|
-
);
|
6380
|
+
assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.NOT_REACH_JBH);
|
6360
6381
|
assert.equal(meeting.requiredCaptcha, null);
|
6361
6382
|
});
|
6362
6383
|
|
@@ -6733,15 +6754,10 @@ describe('plugin-meetings', () => {
|
|
6733
6754
|
meeting.attrs.meetingInfoProvider = {
|
6734
6755
|
fetchMeetingInfo: sinon
|
6735
6756
|
.stub()
|
6736
|
-
.throws(
|
6737
|
-
new MeetingInfoV2JoinWebinarError(403021, FAKE_MEETING_INFO, 'a message')
|
6738
|
-
),
|
6757
|
+
.throws(new MeetingInfoV2JoinWebinarError(403021, FAKE_MEETING_INFO, 'a message')),
|
6739
6758
|
};
|
6740
6759
|
|
6741
|
-
await assert.isRejected(
|
6742
|
-
meeting.fetchMeetingInfo({sendCAevents: true}),
|
6743
|
-
JoinWebinarError
|
6744
|
-
);
|
6760
|
+
await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), JoinWebinarError);
|
6745
6761
|
|
6746
6762
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
6747
6763
|
assert.equal(
|
@@ -6756,15 +6772,10 @@ describe('plugin-meetings', () => {
|
|
6756
6772
|
meeting.attrs.meetingInfoProvider = {
|
6757
6773
|
fetchMeetingInfo: sinon
|
6758
6774
|
.stub()
|
6759
|
-
.throws(
|
6760
|
-
new MeetingInfoV2JoinWebinarError(403026, FAKE_MEETING_INFO, 'a message')
|
6761
|
-
),
|
6775
|
+
.throws(new MeetingInfoV2JoinWebinarError(403026, FAKE_MEETING_INFO, 'a message')),
|
6762
6776
|
};
|
6763
6777
|
|
6764
|
-
await assert.isRejected(
|
6765
|
-
meeting.fetchMeetingInfo({sendCAevents: true}),
|
6766
|
-
JoinWebinarError
|
6767
|
-
);
|
6778
|
+
await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), JoinWebinarError);
|
6768
6779
|
|
6769
6780
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
6770
6781
|
assert.equal(
|
@@ -6779,15 +6790,10 @@ describe('plugin-meetings', () => {
|
|
6779
6790
|
meeting.attrs.meetingInfoProvider = {
|
6780
6791
|
fetchMeetingInfo: sinon
|
6781
6792
|
.stub()
|
6782
|
-
.throws(
|
6783
|
-
new MeetingInfoV2JoinWebinarError(403037, FAKE_MEETING_INFO, 'a message')
|
6784
|
-
),
|
6793
|
+
.throws(new MeetingInfoV2JoinWebinarError(403037, FAKE_MEETING_INFO, 'a message')),
|
6785
6794
|
};
|
6786
6795
|
|
6787
|
-
await assert.isRejected(
|
6788
|
-
meeting.fetchMeetingInfo({sendCAevents: true}),
|
6789
|
-
JoinWebinarError
|
6790
|
-
);
|
6796
|
+
await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), JoinWebinarError);
|
6791
6797
|
|
6792
6798
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
6793
6799
|
assert.equal(
|
@@ -9223,22 +9229,22 @@ describe('plugin-meetings', () => {
|
|
9223
9229
|
const assertBrb = (enabled) => {
|
9224
9230
|
meeting.brbState = createBrbState(meeting, false);
|
9225
9231
|
meeting.locusInfo.emit(
|
9226
|
-
{
|
9232
|
+
{function: 'test', file: 'test'},
|
9227
9233
|
LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED,
|
9228
|
-
{
|
9229
|
-
)
|
9234
|
+
{brb: {enabled}}
|
9235
|
+
);
|
9230
9236
|
assert.calledWithExactly(
|
9231
9237
|
TriggerProxy.trigger,
|
9232
9238
|
meeting,
|
9233
9239
|
{file: 'meeting/index', function: 'setUpLocusInfoSelfListener'},
|
9234
9240
|
EVENT_TRIGGERS.MEETING_SELF_BRB_UPDATE,
|
9235
|
-
{
|
9241
|
+
{payload: {brb: {enabled}}}
|
9236
9242
|
);
|
9237
|
-
}
|
9243
|
+
};
|
9238
9244
|
|
9239
9245
|
assertBrb(true);
|
9240
9246
|
assertBrb(false);
|
9241
|
-
})
|
9247
|
+
});
|
9242
9248
|
|
9243
9249
|
it('listens to the interpretation changed event', () => {
|
9244
9250
|
meeting.simultaneousInterpretation.updateSelfInterpretation = sinon.stub();
|
@@ -11326,18 +11332,21 @@ describe('plugin-meetings', () => {
|
|
11326
11332
|
);
|
11327
11333
|
});
|
11328
11334
|
|
11329
|
-
|
11330
11335
|
it('connect ps data channel if ps started in webinar', async () => {
|
11331
11336
|
meeting.joinedWith = {state: 'JOINED'};
|
11332
|
-
meeting.locusInfo = {
|
11337
|
+
meeting.locusInfo = {
|
11338
|
+
url: 'a url',
|
11339
|
+
info: {
|
11340
|
+
datachannelUrl: 'a datachannel url',
|
11341
|
+
practiceSessionDatachannelUrl: 'a ps datachannel url',
|
11342
|
+
},
|
11343
|
+
};
|
11333
11344
|
meeting.webinar.isJoinPracticeSessionDataChannel = sinon.stub().returns(true);
|
11334
11345
|
await meeting.updateLLMConnection();
|
11335
11346
|
|
11336
11347
|
assert.notCalled(webex.internal.llm.disconnectLLM);
|
11337
11348
|
assert.calledWith(webex.internal.llm.registerAndConnect, 'a url', 'a ps datachannel url');
|
11338
|
-
|
11339
11349
|
});
|
11340
|
-
|
11341
11350
|
});
|
11342
11351
|
|
11343
11352
|
describe('#setLocus', () => {
|
@@ -11755,24 +11764,29 @@ describe('plugin-meetings', () => {
|
|
11755
11764
|
|
11756
11765
|
activeSharingId.whiteboard = beneficiaryId;
|
11757
11766
|
|
11758
|
-
eventTrigger.share.push(
|
11759
|
-
|
11760
|
-
|
11761
|
-
|
11762
|
-
|
11763
|
-
|
11764
|
-
|
11765
|
-
|
11766
|
-
|
11767
|
-
|
11768
|
-
|
11769
|
-
|
11770
|
-
|
11771
|
-
|
11772
|
-
|
11773
|
-
|
11774
|
-
|
11767
|
+
eventTrigger.share.push(
|
11768
|
+
meeting.webinar.selfIsAttendee
|
11769
|
+
? {
|
11770
|
+
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
11771
|
+
functionName: 'remoteShare',
|
11772
|
+
eventPayload: {
|
11773
|
+
memberId: null,
|
11774
|
+
url,
|
11775
|
+
shareInstanceId,
|
11776
|
+
annotationInfo: undefined,
|
11777
|
+
resourceType: undefined,
|
11778
|
+
},
|
11779
|
+
}
|
11780
|
+
: {
|
11781
|
+
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
|
11782
|
+
functionName: 'startWhiteboardShare',
|
11783
|
+
eventPayload: {resourceUrl, memberId: beneficiaryId},
|
11784
|
+
}
|
11785
|
+
);
|
11775
11786
|
|
11787
|
+
shareStatus = meeting.webinar.selfIsAttendee
|
11788
|
+
? SHARE_STATUS.REMOTE_SHARE_ACTIVE
|
11789
|
+
: SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
11776
11790
|
}
|
11777
11791
|
|
11778
11792
|
if (eventTrigger.member) {
|
@@ -11804,24 +11818,29 @@ describe('plugin-meetings', () => {
|
|
11804
11818
|
newPayload.current.content.disposition = FLOOR_ACTION.ACCEPTED;
|
11805
11819
|
newPayload.current.content.beneficiaryId = otherBeneficiaryId;
|
11806
11820
|
|
11807
|
-
eventTrigger.share.push(
|
11808
|
-
|
11809
|
-
|
11810
|
-
|
11811
|
-
|
11812
|
-
|
11813
|
-
|
11814
|
-
|
11815
|
-
|
11816
|
-
|
11817
|
-
|
11818
|
-
|
11819
|
-
|
11820
|
-
|
11821
|
-
|
11822
|
-
|
11823
|
-
|
11821
|
+
eventTrigger.share.push(
|
11822
|
+
meeting.webinar.selfIsAttendee
|
11823
|
+
? {
|
11824
|
+
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
11825
|
+
functionName: 'remoteShare',
|
11826
|
+
eventPayload: {
|
11827
|
+
memberId: null,
|
11828
|
+
url,
|
11829
|
+
shareInstanceId,
|
11830
|
+
annotationInfo: undefined,
|
11831
|
+
resourceType: undefined,
|
11832
|
+
},
|
11833
|
+
}
|
11834
|
+
: {
|
11835
|
+
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
|
11836
|
+
functionName: 'startWhiteboardShare',
|
11837
|
+
eventPayload: {resourceUrl, memberId: beneficiaryId},
|
11838
|
+
}
|
11839
|
+
);
|
11824
11840
|
|
11841
|
+
shareStatus = meeting.webinar.selfIsAttendee
|
11842
|
+
? SHARE_STATUS.REMOTE_SHARE_ACTIVE
|
11843
|
+
: SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
11825
11844
|
} else {
|
11826
11845
|
eventTrigger.share.push({
|
11827
11846
|
eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_WHITEBOARD,
|
@@ -11951,24 +11970,24 @@ describe('plugin-meetings', () => {
|
|
11951
11970
|
describe('Whiteboard Share - Webinar Attendee', () => {
|
11952
11971
|
it('Scenario #1: Whiteboard sharing as a webinar attendee', () => {
|
11953
11972
|
// Set the webinar attendee flag
|
11954
|
-
meeting.webinar = {
|
11973
|
+
meeting.webinar = {selfIsAttendee: true};
|
11955
11974
|
meeting.locusInfo.info.isWebinar = true;
|
11956
11975
|
|
11957
11976
|
// Step 1: Start sharing whiteboard A
|
11958
11977
|
const data1 = generateData(
|
11959
|
-
blankPayload,
|
11960
|
-
true,
|
11961
|
-
false,
|
11962
|
-
USER_IDS.REMOTE_A,
|
11978
|
+
blankPayload, // Initial payload
|
11979
|
+
true, // isGranting: Granting share
|
11980
|
+
false, // isContent: Whiteboard (not content)
|
11981
|
+
USER_IDS.REMOTE_A, // Beneficiary ID: Remote user A
|
11963
11982
|
RESOURCE_URLS.WHITEBOARD_A // Resource URL: Whiteboard A
|
11964
11983
|
);
|
11965
11984
|
|
11966
11985
|
// Step 2: Stop sharing whiteboard A
|
11967
11986
|
const data2 = generateData(
|
11968
|
-
data1.payload,
|
11969
|
-
false,
|
11970
|
-
false,
|
11971
|
-
USER_IDS.REMOTE_A
|
11987
|
+
data1.payload, // Updated payload from Step 1
|
11988
|
+
false, // isGranting: Stopping share
|
11989
|
+
false, // isContent: Whiteboard
|
11990
|
+
USER_IDS.REMOTE_A // Beneficiary ID: Remote user A
|
11972
11991
|
);
|
11973
11992
|
|
11974
11993
|
// Validate the payload changes and status updates
|
@@ -11979,7 +11998,6 @@ describe('plugin-meetings', () => {
|
|
11979
11998
|
});
|
11980
11999
|
});
|
11981
12000
|
|
11982
|
-
|
11983
12001
|
describe('Whiteboard A --> Whiteboard B', () => {
|
11984
12002
|
it('Scenario #1: you share both whiteboards', () => {
|
11985
12003
|
const data1 = generateData(
|
@@ -13290,7 +13308,7 @@ describe('plugin-meetings', () => {
|
|
13290
13308
|
await meeting.roapMessageReceived(fakeMessage);
|
13291
13309
|
|
13292
13310
|
assert.fail('Expected MultistreamNotSupportedError to be thrown');
|
13293
|
-
} catch(e) {
|
13311
|
+
} catch (e) {
|
13294
13312
|
assert.isTrue(e instanceof MultistreamNotSupportedError);
|
13295
13313
|
}
|
13296
13314
|
|
@@ -1,14 +1,18 @@
|
|
1
1
|
import 'jsdom-global/register';
|
2
2
|
import sinon from 'sinon';
|
3
3
|
import {assert} from '@webex/test-helper-chai';
|
4
|
-
import {
|
5
|
-
|
4
|
+
import {cloneDeep} from 'lodash';
|
5
|
+
import {EventEmitter} from 'events';
|
6
6
|
import MockWebex from '@webex/test-helper-mock-webex';
|
7
7
|
import Meetings from '@webex/plugin-meetings';
|
8
|
-
import {
|
8
|
+
import {
|
9
|
+
LocalMuteRequest,
|
10
|
+
LocusMediaRequest,
|
11
|
+
RoapRequest,
|
12
|
+
} from '@webex/plugin-meetings/src/meeting/locusMediaRequest';
|
9
13
|
import testUtils from '../../../utils/testUtils';
|
10
|
-
import {
|
11
|
-
import {
|
14
|
+
import {Defer} from '@webex/common';
|
15
|
+
import {IP_VERSION} from '../../../../src/constants';
|
12
16
|
|
13
17
|
describe('LocusMediaRequest.send()', () => {
|
14
18
|
let locusMediaRequest: LocusMediaRequest;
|
@@ -16,10 +20,10 @@ describe('LocusMediaRequest.send()', () => {
|
|
16
20
|
let mockWebex;
|
17
21
|
|
18
22
|
const fakeLocusResponse = {
|
19
|
-
locus: {
|
23
|
+
locus: {something: 'whatever'},
|
20
24
|
};
|
21
25
|
|
22
|
-
const exampleRoapRequestBody:RoapRequest = {
|
26
|
+
const exampleRoapRequestBody: RoapRequest = {
|
23
27
|
type: 'RoapMessage',
|
24
28
|
mediaId: 'mediaId',
|
25
29
|
selfUrl: 'fakeMeetingSelfUrl',
|
@@ -31,8 +35,11 @@ describe('LocusMediaRequest.send()', () => {
|
|
31
35
|
tieBreaker: 0xfffffffe,
|
32
36
|
},
|
33
37
|
reachability: {
|
34
|
-
'wjfkm.wjfkm.*': {udp:{reachable: true}, tcp:{reachable:false}},
|
35
|
-
'1eb65fdf-9643-417f-9974-ad72cae0e10f.59268c12-7a04-4b23-a1a1-4c74be03019a.*': {
|
38
|
+
'wjfkm.wjfkm.*': {udp: {reachable: true}, tcp: {reachable: false}},
|
39
|
+
'1eb65fdf-9643-417f-9974-ad72cae0e10f.59268c12-7a04-4b23-a1a1-4c74be03019a.*': {
|
40
|
+
udp: {reachable: false},
|
41
|
+
tcp: {reachable: true},
|
42
|
+
},
|
36
43
|
},
|
37
44
|
clientMediaPreferences: {
|
38
45
|
preferTranscoding: false,
|
@@ -45,19 +52,22 @@ describe('LocusMediaRequest.send()', () => {
|
|
45
52
|
reachability: {
|
46
53
|
version: '1',
|
47
54
|
result: 'some fake reachability result',
|
48
|
-
}
|
49
|
-
}
|
55
|
+
},
|
56
|
+
},
|
50
57
|
};
|
51
58
|
|
52
|
-
const createExpectedRoapBody = (
|
59
|
+
const createExpectedRoapBody = (
|
60
|
+
expectedMessageType,
|
61
|
+
expectedMute: {audioMuted: boolean; videoMuted: boolean}
|
62
|
+
) => {
|
53
63
|
return {
|
54
|
-
device: {
|
64
|
+
device: {url: 'deviceUrl', deviceType: 'deviceType', regionCode: 'regionCode'},
|
55
65
|
correlationId: 'correlationId',
|
56
66
|
localMedias: [
|
57
67
|
{
|
58
68
|
localSdp: `{"audioMuted":${expectedMute.audioMuted},"videoMuted":${expectedMute.videoMuted},"roapMessage":{"messageType":"${expectedMessageType}","sdps":["sdp"],"version":"2","seq":1,"tieBreaker":4294967294},"reachability":{"wjfkm.wjfkm.*":{"udp":{"reachable":true},"tcp":{"reachable":false}},"1eb65fdf-9643-417f-9974-ad72cae0e10f.59268c12-7a04-4b23-a1a1-4c74be03019a.*":{"udp":{"reachable":false},"tcp":{"reachable":true}}}}`,
|
59
|
-
mediaId: 'mediaId'
|
60
|
-
}
|
69
|
+
mediaId: 'mediaId',
|
70
|
+
},
|
61
71
|
],
|
62
72
|
clientMediaPreferences: {
|
63
73
|
preferTranscoding: false,
|
@@ -65,24 +75,27 @@ describe('LocusMediaRequest.send()', () => {
|
|
65
75
|
joinCookie: {
|
66
76
|
anycastEntryPoint: 'aws-eu-west-1',
|
67
77
|
clientIpAddress: 'some ip',
|
68
|
-
timeShot: '2023-05-23T08:03:49Z'
|
78
|
+
timeShot: '2023-05-23T08:03:49Z',
|
69
79
|
},
|
70
80
|
reachability: {
|
71
81
|
version: '1',
|
72
82
|
result: 'some fake reachability result',
|
73
|
-
}
|
74
|
-
}
|
83
|
+
},
|
84
|
+
},
|
75
85
|
};
|
76
86
|
};
|
77
87
|
|
78
|
-
const exampleLocalMuteRequestBody:LocalMuteRequest = {
|
88
|
+
const exampleLocalMuteRequestBody: LocalMuteRequest = {
|
79
89
|
type: 'LocalMute',
|
80
90
|
mediaId: 'mediaId',
|
81
91
|
selfUrl: 'fakeMeetingSelfUrl',
|
82
92
|
muteOptions: {},
|
83
93
|
};
|
84
94
|
|
85
|
-
const createExpectedLocalMuteBody = (
|
95
|
+
const createExpectedLocalMuteBody = (
|
96
|
+
expectedMute: {audioMuted: boolean; videoMuted: boolean},
|
97
|
+
sequence = undefined
|
98
|
+
) => {
|
86
99
|
const body: any = {
|
87
100
|
device: {
|
88
101
|
url: 'deviceUrl',
|
@@ -116,33 +129,37 @@ describe('LocusMediaRequest.send()', () => {
|
|
116
129
|
|
117
130
|
mockWebex.internal = {
|
118
131
|
newMetrics: {
|
119
|
-
submitClientEvent: sinon.stub()
|
132
|
+
submitClientEvent: sinon.stub(),
|
120
133
|
},
|
121
134
|
};
|
122
135
|
|
123
|
-
locusMediaRequest = new LocusMediaRequest(
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
136
|
+
locusMediaRequest = new LocusMediaRequest(
|
137
|
+
{
|
138
|
+
device: {
|
139
|
+
url: 'deviceUrl',
|
140
|
+
deviceType: 'deviceType',
|
141
|
+
regionCode: 'regionCode',
|
142
|
+
},
|
143
|
+
correlationId: 'correlationId',
|
144
|
+
meetingId: 'meetingId',
|
145
|
+
preferTranscoding: true,
|
128
146
|
},
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
parent: mockWebex,
|
134
|
-
});
|
147
|
+
{
|
148
|
+
parent: mockWebex,
|
149
|
+
}
|
150
|
+
);
|
135
151
|
webexRequestStub = sinon.stub(locusMediaRequest, 'request').resolves(fakeLocusResponse);
|
136
|
-
})
|
152
|
+
});
|
137
153
|
|
138
|
-
const sendLocalMute = (muteOptions, overrides={}) =>
|
154
|
+
const sendLocalMute = (muteOptions, overrides = {}) =>
|
155
|
+
locusMediaRequest.send({...exampleLocalMuteRequestBody, ...overrides, muteOptions});
|
139
156
|
|
140
157
|
const sendRoapMessage = (messageType) => {
|
141
158
|
const request = cloneDeep(exampleRoapRequestBody);
|
142
159
|
|
143
160
|
request.roapMessage.messageType = messageType;
|
144
161
|
return locusMediaRequest.send(request);
|
145
|
-
}
|
162
|
+
};
|
146
163
|
|
147
164
|
/** Helper function that makes sure the LocusMediaRequest.confluenceState is 'created' */
|
148
165
|
const ensureConfluenceCreated = async () => {
|
@@ -150,7 +167,7 @@ describe('LocusMediaRequest.send()', () => {
|
|
150
167
|
|
151
168
|
webexRequestStub.resetHistory();
|
152
169
|
mockWebex.internal.newMetrics.submitClientEvent.resetHistory();
|
153
|
-
}
|
170
|
+
};
|
154
171
|
|
155
172
|
const checkMetrics = (expectedMetrics: boolean = true) => {
|
156
173
|
if (expectedMetrics) {
|
@@ -170,7 +187,7 @@ describe('LocusMediaRequest.send()', () => {
|
|
170
187
|
} else {
|
171
188
|
assert.notCalled(mockWebex.internal.newMetrics.submitClientEvent);
|
172
189
|
}
|
173
|
-
}
|
190
|
+
};
|
174
191
|
|
175
192
|
it('sends a roap message', async () => {
|
176
193
|
const result = await sendRoapMessage('OFFER');
|
@@ -181,6 +198,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
181
198
|
method: 'PUT',
|
182
199
|
uri: 'fakeMeetingSelfUrl/media',
|
183
200
|
body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: true}),
|
201
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
202
|
+
download: sinon.match.instanceOf(EventEmitter),
|
184
203
|
});
|
185
204
|
|
186
205
|
checkMetrics();
|
@@ -210,6 +229,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
210
229
|
method: 'PUT',
|
211
230
|
uri: 'fakeMeetingSelfUrl/media',
|
212
231
|
body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: false}),
|
232
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
233
|
+
download: sinon.match.instanceOf(EventEmitter),
|
213
234
|
});
|
214
235
|
|
215
236
|
checkMetrics(false);
|
@@ -228,6 +249,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
228
249
|
method: 'PUT',
|
229
250
|
uri: 'fakeMeetingSelfUrl/media',
|
230
251
|
body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: false}, sequence),
|
252
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
253
|
+
download: sinon.match.instanceOf(EventEmitter),
|
231
254
|
});
|
232
255
|
});
|
233
256
|
|
@@ -237,15 +260,13 @@ describe('LocusMediaRequest.send()', () => {
|
|
237
260
|
let result1;
|
238
261
|
let result2;
|
239
262
|
|
240
|
-
const promise1 = sendLocalMute({audioMuted: true, videoMuted: false})
|
241
|
-
|
242
|
-
|
243
|
-
});
|
263
|
+
const promise1 = sendLocalMute({audioMuted: true, videoMuted: false}).then((result) => {
|
264
|
+
result1 = result;
|
265
|
+
});
|
244
266
|
|
245
|
-
const promise2 = sendLocalMute({audioMuted: false, videoMuted: true})
|
246
|
-
|
247
|
-
|
248
|
-
});
|
267
|
+
const promise2 = sendLocalMute({audioMuted: false, videoMuted: true}).then((result) => {
|
268
|
+
result2 = result;
|
269
|
+
});
|
249
270
|
|
250
271
|
await testUtils.flushPromises();
|
251
272
|
|
@@ -258,6 +279,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
258
279
|
method: 'PUT',
|
259
280
|
uri: 'fakeMeetingSelfUrl/media',
|
260
281
|
body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: true}),
|
282
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
283
|
+
download: sinon.match.instanceOf(EventEmitter),
|
261
284
|
});
|
262
285
|
|
263
286
|
checkMetrics(false);
|
@@ -277,6 +300,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
277
300
|
method: 'PUT',
|
278
301
|
uri: 'fakeMeetingSelfUrl/media',
|
279
302
|
body: createExpectedLocalMuteBody({audioMuted: true, videoMuted: false}),
|
303
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
304
|
+
download: sinon.match.instanceOf(EventEmitter),
|
280
305
|
});
|
281
306
|
|
282
307
|
checkMetrics(false);
|
@@ -296,6 +321,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
296
321
|
method: 'PUT',
|
297
322
|
uri: 'fakeMeetingSelfUrl/media',
|
298
323
|
body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: false}),
|
324
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
325
|
+
download: sinon.match.instanceOf(EventEmitter),
|
299
326
|
});
|
300
327
|
|
301
328
|
checkMetrics();
|
@@ -328,10 +355,10 @@ describe('LocusMediaRequest.send()', () => {
|
|
328
355
|
* after the processing cycle from which it was called is finished.
|
329
356
|
* This helper function waits for this to happen - it's needed, because we're using
|
330
357
|
* fake timers in these tests
|
331
|
-
|
358
|
+
*/
|
332
359
|
const ensureQueueProcessingIsStarted = () => {
|
333
360
|
clock.tick(1);
|
334
|
-
}
|
361
|
+
};
|
335
362
|
it('queues requests if there is one already in progress', async () => {
|
336
363
|
results.push(sendRoapMessage('OFFER'));
|
337
364
|
|
@@ -342,6 +369,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
342
369
|
method: 'PUT',
|
343
370
|
uri: 'fakeMeetingSelfUrl/media',
|
344
371
|
body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: true}),
|
372
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
373
|
+
download: sinon.match.instanceOf(EventEmitter),
|
345
374
|
});
|
346
375
|
|
347
376
|
webexRequestStub.resetHistory();
|
@@ -364,6 +393,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
364
393
|
method: 'PUT',
|
365
394
|
uri: 'fakeMeetingSelfUrl/media',
|
366
395
|
body: createExpectedRoapBody('OK', {audioMuted: true, videoMuted: true}),
|
396
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
397
|
+
download: sinon.match.instanceOf(EventEmitter),
|
367
398
|
});
|
368
399
|
|
369
400
|
// promise returned by the first call to send OFFER should be resolved by now
|
@@ -386,6 +417,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
386
417
|
method: 'PUT',
|
387
418
|
uri: 'fakeMeetingSelfUrl/media',
|
388
419
|
body: createExpectedRoapBody('OFFER', {audioMuted: false, videoMuted: false}),
|
420
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
421
|
+
download: sinon.match.instanceOf(EventEmitter),
|
389
422
|
});
|
390
423
|
|
391
424
|
webexRequestStub.resetHistory();
|
@@ -410,6 +443,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
410
443
|
method: 'PUT',
|
411
444
|
uri: 'fakeMeetingSelfUrl/media',
|
412
445
|
body: createExpectedRoapBody('OK', {audioMuted: false, videoMuted: true}),
|
446
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
447
|
+
download: sinon.match.instanceOf(EventEmitter),
|
413
448
|
});
|
414
449
|
|
415
450
|
// promise returned by the first call to send OFFER should be resolved by now
|
@@ -438,16 +473,17 @@ describe('LocusMediaRequest.send()', () => {
|
|
438
473
|
|
439
474
|
ensureQueueProcessingIsStarted();
|
440
475
|
|
441
|
-
sendLocalMute({audioMuted: false, videoMuted: true})
|
442
|
-
|
443
|
-
|
444
|
-
});
|
476
|
+
sendLocalMute({audioMuted: false, videoMuted: true}).then((response) => {
|
477
|
+
result = response;
|
478
|
+
});
|
445
479
|
|
446
480
|
// only roap offer should have been sent so far
|
447
481
|
assert.calledOnceWithExactly(webexRequestStub, {
|
448
482
|
method: 'PUT',
|
449
483
|
uri: 'fakeMeetingSelfUrl/media',
|
450
484
|
body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: true}),
|
485
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
486
|
+
download: sinon.match.instanceOf(EventEmitter),
|
451
487
|
});
|
452
488
|
assert.equal(result, undefined); // sendLocalMute shouldn't resolve yet, as the request should be queued
|
453
489
|
assert.equal(locusMediaRequest.isConfluenceCreated(), false);
|
@@ -464,10 +500,12 @@ describe('LocusMediaRequest.send()', () => {
|
|
464
500
|
method: 'PUT',
|
465
501
|
uri: 'fakeMeetingSelfUrl/media',
|
466
502
|
body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: true}),
|
503
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
504
|
+
download: sinon.match.instanceOf(EventEmitter),
|
467
505
|
});
|
468
506
|
|
469
507
|
// check also the result once Locus replies to local mute
|
470
|
-
const fakeLocusResponse = {
|
508
|
+
const fakeLocusResponse = {response: 'ok'};
|
471
509
|
requestsToLocus[1].resolve(fakeLocusResponse);
|
472
510
|
await testUtils.flushPromises();
|
473
511
|
assert.deepEqual(result, fakeLocusResponse);
|
@@ -487,10 +525,9 @@ describe('LocusMediaRequest.send()', () => {
|
|
487
525
|
assert.equal(locusMediaRequest.isConfluenceCreated(), true);
|
488
526
|
|
489
527
|
// now send local mute
|
490
|
-
sendLocalMute({audioMuted: false, videoMuted: true})
|
491
|
-
|
492
|
-
|
493
|
-
});
|
528
|
+
sendLocalMute({audioMuted: false, videoMuted: true}).then((response) => {
|
529
|
+
result = response;
|
530
|
+
});
|
494
531
|
|
495
532
|
ensureQueueProcessingIsStarted();
|
496
533
|
|
@@ -499,8 +536,9 @@ describe('LocusMediaRequest.send()', () => {
|
|
499
536
|
method: 'PUT',
|
500
537
|
uri: 'fakeMeetingSelfUrl/media',
|
501
538
|
body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: true}),
|
539
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
540
|
+
download: sinon.match.instanceOf(EventEmitter),
|
502
541
|
});
|
503
542
|
});
|
504
|
-
|
505
543
|
});
|
506
|
-
})
|
544
|
+
});
|