diff --git a/packages/@webex/internal-plugin-metrics/package.json b/packages/@webex/internal-plugin-metrics/package.json index bb79fb7c8c7..c5252aa475e 100644 --- a/packages/@webex/internal-plugin-metrics/package.json +++ b/packages/@webex/internal-plugin-metrics/package.json @@ -37,7 +37,7 @@ "dependencies": { "@webex/common": "workspace:*", "@webex/common-timers": "workspace:*", - "@webex/event-dictionary-ts": "^1.0.1401", + "@webex/event-dictionary-ts": "^1.0.1406", "@webex/internal-plugin-device": "workspace:*", "@webex/internal-plugin-metrics": "workspace:*", "@webex/test-helper-chai": "workspace:*", diff --git a/packages/@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics-latencies.ts b/packages/@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics-latencies.ts index 64601981d10..9f3fba8ae92 100644 --- a/packages/@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +++ b/packages/@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics-latencies.ts @@ -455,6 +455,15 @@ export default class CallDiagnosticLatencies extends WebexPlugin { return this.getDiffBetweenTimestamps('client.locus.join.response', 'client.media.tx.start'); } + /** + * Total latency for all exchange ci token. + */ + public getExchangeCITokenJMT() { + const exchangeCITokenJMT = this.precomputedLatencies.get('internal.exchange.ci.token.time'); + + return exchangeCITokenJMT ? Math.floor(exchangeCITokenJMT) : undefined; + } + /** * Total latency for all refresh captcha requests. */ diff --git a/packages/@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics.ts b/packages/@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics.ts index 81bf12c7b7c..f31a41063f0 100644 --- a/packages/@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics.ts +++ b/packages/@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics.ts @@ -39,6 +39,7 @@ import { ClientInfo, ClientEventPayloadError, ClientSubServiceType, + BrowserLaunchMethodType, } from '../metrics.types'; import CallDiagnosticEventsBatcher from './call-diagnostic-metrics-batcher'; import PreLoginMetricsBatcher from '../prelogin-metrics-batcher'; @@ -65,6 +66,7 @@ type GetOriginOptions = { subClientType: SubClientType; networkType?: NetworkType; clientLaunchMethod?: ClientLaunchMethodType; + browserLaunchMethod?: BrowserLaunchMethodType; environment?: EnvironmentType; newEnvironment?: NewEnvironmentType; }; @@ -258,6 +260,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin { origin.clientInfo.clientLaunchMethod = options.clientLaunchMethod; } + if (options?.browserLaunchMethod) { + origin.clientInfo.browserLaunchMethod = options.browserLaunchMethod; + } + return origin; } diff --git a/packages/@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics.util.ts b/packages/@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics.util.ts index ed4ea758a03..a46d9695e8b 100644 --- a/packages/@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics.util.ts +++ b/packages/@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics.util.ts @@ -246,6 +246,7 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => { break; case 'client.login.end': joinTimes.otherAppApiReqResp = cdl.getOtherAppApiReqResp(); + joinTimes.exchangeCITokenJMT = cdl.getExchangeCITokenJMT(); break; case 'client.interstitial-window.launched': joinTimes.meetingInfoReqResp = cdl.getMeetingInfoReqResp(); diff --git a/packages/@webex/internal-plugin-metrics/src/metrics.types.ts b/packages/@webex/internal-plugin-metrics/src/metrics.types.ts index 4774f440dc4..7ef1d91bb1c 100644 --- a/packages/@webex/internal-plugin-metrics/src/metrics.types.ts +++ b/packages/@webex/internal-plugin-metrics/src/metrics.types.ts @@ -16,6 +16,10 @@ export type ClientLaunchMethodType = NonNullable< RawEvent['origin']['clientInfo'] >['clientLaunchMethod']; +export type BrowserLaunchMethodType = NonNullable< + RawEvent['origin']['clientInfo'] +>['browserLaunchMethod']; + export type SubmitClientEventOptions = { meetingId?: string; mediaConnections?: any[]; @@ -25,6 +29,7 @@ export type SubmitClientEventOptions = { environment?: EnvironmentType; newEnvironmentType?: NewEnvironmentType; clientLaunchMethod?: ClientLaunchMethodType; + browserLaunchMethod?: BrowserLaunchMethodType; webexConferenceIdStr?: string; globalMeetingId?: string; }; @@ -166,6 +171,7 @@ export type PreComputedLatencies = | 'internal.download.time' | 'internal.click.to.interstitial' | 'internal.refresh.captcha.time' + | 'internal.exchange.ci.token.time' | 'internal.get.u2c.time' | 'internal.call.init.join.req' | 'internal.other.app.api.time'; diff --git a/packages/@webex/internal-plugin-metrics/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts b/packages/@webex/internal-plugin-metrics/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts index f4276f14f9e..67fa93bd4f5 100644 --- a/packages/@webex/internal-plugin-metrics/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +++ b/packages/@webex/internal-plugin-metrics/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts @@ -224,6 +224,24 @@ describe('internal-plugin-metrics', () => { }); }); + describe('getExchangeCITokenJMT', () => { + it('returns undefined when no precomputed value available', () => { + assert.deepEqual(cdl.getExchangeCITokenJMT(), undefined); + }); + + it('returns the correct value', () => { + cdl.saveLatency('internal.exchange.ci.token.time', 123); + + assert.deepEqual(cdl.getExchangeCITokenJMT(), 123); + }); + + it('returns the correct whole number', () => { + cdl.saveLatency('internal.exchange.ci.token.time', 321.44); + + assert.deepEqual(cdl.getExchangeCITokenJMT(), 321); + }); + }); + describe('saveTimestamp', () => { afterEach(() => { sinon.restore(); diff --git a/packages/@webex/internal-plugin-metrics/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts b/packages/@webex/internal-plugin-metrics/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts index fccb9325244..af3c1cb0707 100644 --- a/packages/@webex/internal-plugin-metrics/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +++ b/packages/@webex/internal-plugin-metrics/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts @@ -223,6 +223,43 @@ describe('internal-plugin-metrics', () => { }); }); + it('should build origin correctly with browserLaunchMethod', () => { + sinon.stub(CallDiagnosticUtils, 'anonymizeIPAddress').returns('1.1.1.1'); + + //@ts-ignore + const res = cd.getOrigin( + { + subClientType: 'WEB_APP', + clientType: 'TEAMS_CLIENT', + newEnvironment: 'test-new-env', + clientLaunchMethod: 'url-handler', + browserLaunchMethod: 'thinclient', + }, + fakeMeeting.id + ); + + assert.deepEqual(res, { + clientInfo: { + browser: getBrowserName(), + browserVersion: getBrowserVersion(), + clientType: 'TEAMS_CLIENT', + clientVersion: 'webex-js-sdk/webex-version', + publicNetworkPrefix: '1.1.1.1', + localNetworkPrefix: '1.1.1.1', + os: getOSNameInternal(), + osVersion: getOSVersion(), + subClientType: 'WEB_APP', + clientLaunchMethod: 'url-handler', + browserLaunchMethod: 'thinclient', + }, + environment: 'meeting_evn', + newEnvironment: 'test-new-env', + name: 'endpoint', + networkType: 'unknown', + userAgent, + }); + }); + it('should build origin correctly with no meeting', () => { sinon.stub(CallDiagnosticUtils, 'anonymizeIPAddress').returns('1.1.1.1'); diff --git a/packages/@webex/internal-plugin-metrics/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts b/packages/@webex/internal-plugin-metrics/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts index 923181e401e..0eca3325146 100644 --- a/packages/@webex/internal-plugin-metrics/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +++ b/packages/@webex/internal-plugin-metrics/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts @@ -304,6 +304,7 @@ describe('internal-plugin-metrics', () => { ['client.login.end', { joinTimes: { otherAppApiReqResp: undefined, + exchangeCITokenJMT: undefined, } }], ['client.webexapp.launched', { diff --git a/packages/@webex/media-helpers/package.json b/packages/@webex/media-helpers/package.json index 95b5d99e243..12f7567c13a 100644 --- a/packages/@webex/media-helpers/package.json +++ b/packages/@webex/media-helpers/package.json @@ -24,7 +24,7 @@ "dependencies": { "@webex/internal-media-core": "2.3.2", "@webex/ts-events": "^1.1.0", - "@webex/web-media-effects": "^2.15.6" + "@webex/web-media-effects": "2.18.0" }, "browserify": { "transform": [ diff --git a/packages/@webex/plugin-meetings/src/constants.ts b/packages/@webex/plugin-meetings/src/constants.ts index 898104a542b..60c2c8c5057 100644 --- a/packages/@webex/plugin-meetings/src/constants.ts +++ b/packages/@webex/plugin-meetings/src/constants.ts @@ -196,7 +196,7 @@ export const ICE_FAIL_TIMEOUT = 3000; export const RETRY_TIMEOUT = 3000; -export const ICE_AND_DTLS_CONNECTION_TIMEOUT = 10000; +export const ICE_AND_DTLS_CONNECTION_TIMEOUT = 20000; export const ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT = 35000; // ******************** REGEX ********************** @@ -972,7 +972,7 @@ export const SELF_ROLES = { COHOST: 'COHOST', MODERATOR: 'MODERATOR', ATTENDEE: 'ATTENDEE', - PRESENTER: 'PRESENTER', + PANELIST: 'PANELIST', }; export const MEETING_STATE = { diff --git a/packages/@webex/plugin-meetings/src/meeting/index.ts b/packages/@webex/plugin-meetings/src/meeting/index.ts index 78f0dc6cf22..3bbf9494f02 100644 --- a/packages/@webex/plugin-meetings/src/meeting/index.ts +++ b/packages/@webex/plugin-meetings/src/meeting/index.ts @@ -10,6 +10,8 @@ import { ClientEventLeaveReason, CallDiagnosticUtils, } from '@webex/internal-plugin-metrics'; +import {ClientEvent as RawClientEvent} from '@webex/event-dictionary-ts'; + import { ConnectionState, Errors, @@ -6052,6 +6054,20 @@ export default class Meeting extends StatelessWebexPlugin { meetingId: this.id, }, }); + + if (data.type === 'share') { + // @ts-ignore + this.webex.internal.newMetrics.submitClientEvent({ + name: 'client.media.render.start', + payload: { + mediaType: 'share', + shareInstanceId: this.remoteShareInstanceId, + }, + options: { + meetingId: this.id, + }, + }); + } }); this.statsAnalyzer.on(StatsAnalyzerEvents.REMOTE_MEDIA_STOPPED, (data) => { // @ts-ignore @@ -6065,6 +6081,20 @@ export default class Meeting extends StatelessWebexPlugin { meetingId: this.id, }, }); + + if (data.type === 'share') { + // @ts-ignore + this.webex.internal.newMetrics.submitClientEvent({ + name: 'client.media.render.stop', + payload: { + mediaType: 'share', + shareInstanceId: this.remoteShareInstanceId, + }, + options: { + meetingId: this.id, + }, + }); + } }); }; @@ -7913,9 +7943,9 @@ export default class Meeting extends StatelessWebexPlugin { /** * - * @returns {string} one of 'attendee','host','cohost', returns the user type of the current user + * @returns {string} one of 'panelist', 'attendee', 'host', 'cohost', returns the user type of the current user */ - getCurUserType() { + getCurUserType(): RawClientEvent['userType'] | null { const {roles} = this; if (roles) { if (roles.includes(SELF_ROLES.MODERATOR)) { @@ -7924,8 +7954,8 @@ export default class Meeting extends StatelessWebexPlugin { if (roles.includes(SELF_ROLES.COHOST)) { return 'cohost'; } - if (roles.includes(SELF_ROLES.PRESENTER)) { - return 'presenter'; + if (roles.includes(SELF_ROLES.PANELIST)) { + return 'panelist'; } if (roles.includes(SELF_ROLES.ATTENDEE)) { return 'attendee'; diff --git a/packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js b/packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js index 57dd8d68754..001a8b65c98 100644 --- a/packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js +++ b/packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js @@ -109,9 +109,7 @@ import CallDiagnosticMetrics from '@webex/internal-plugin-metrics/src/call-diagn import {ERROR_DESCRIPTIONS} from '@webex/internal-plugin-metrics/src/call-diagnostic/config'; import MeetingCollection from '@webex/plugin-meetings/src/meetings/collection'; -import { - EVENT_TRIGGERS as VOICEAEVENTS, -} from '@webex/internal-plugin-voicea'; +import {EVENT_TRIGGERS as VOICEAEVENTS} from '@webex/internal-plugin-voicea'; describe('plugin-meetings', () => { const logger = { @@ -686,7 +684,10 @@ describe('plugin-meetings', () => { }); it('should call abortTurnDiscovery() if we do not get a TURN server info', async () => { - handleTurnDiscoveryHttpResponseStub.resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: 'missing http response'}); + handleTurnDiscoveryHttpResponseStub.resolves({ + turnServerInfo: undefined, + turnDiscoverySkippedReason: 'missing http response', + }); const result = await meeting.joinWithMedia({ joinOptions, @@ -720,7 +721,8 @@ describe('plugin-meetings', () => { assert.calledOnceWithExactly(abortTurnDiscoveryStub); - assert.calledWith(Metrics.sendBehavioralMetric, + assert.calledWith( + Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE, { correlation_id: meeting.correlationId, @@ -765,7 +767,8 @@ describe('plugin-meetings', () => { reason: 'joinWithMedia failure', }); - assert.calledWith(Metrics.sendBehavioralMetric, + assert.calledWith( + Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE, { correlation_id: meeting.correlationId, @@ -804,7 +807,7 @@ describe('plugin-meetings', () => { it('should subscribe to events for the first time and avoid subscribing for future transcription starts', async () => { meeting.joinedWith = { - state: 'JOINED' + state: 'JOINED', }; meeting.areVoiceaEventsSetup = false; meeting.roles = ['MODERATOR']; @@ -814,27 +817,19 @@ describe('plugin-meetings', () => { assert.equal(webex.internal.voicea.on.callCount, 4); assert.equal(meeting.areVoiceaEventsSetup, true); assert.equal(webex.internal.voicea.listenToEvents.callCount, 1); - assert.calledWith( - webex.internal.voicea.toggleTranscribing, - true, - ); + assert.calledWith(webex.internal.voicea.toggleTranscribing, true); await meeting.startTranscription(); assert.equal(webex.internal.voicea.on.callCount, 4); assert.equal(meeting.areVoiceaEventsSetup, true); assert.equal(webex.internal.voicea.listenToEvents.callCount, 1); - assert.calledTwice( - webex.internal.voicea.toggleTranscribing, - ); - assert.calledWith( - webex.internal.voicea.toggleTranscribing, - true, - ); + assert.calledTwice(webex.internal.voicea.toggleTranscribing); + assert.calledWith(webex.internal.voicea.toggleTranscribing, true); }); it('should listen to events and not toggleTranscribing if the user is not a host', async () => { meeting.joinedWith = { - state: 'JOINED' + state: 'JOINED', }; meeting.areVoiceaEventsSetup = false; meeting.roles = ['COHOST']; @@ -844,9 +839,7 @@ describe('plugin-meetings', () => { assert.equal(webex.internal.voicea.on.callCount, 4); assert.equal(meeting.areVoiceaEventsSetup, true); assert.equal(webex.internal.voicea.listenToEvents.callCount, 1); - assert.notCalled( - webex.internal.voicea.toggleTranscribing - ); + assert.notCalled(webex.internal.voicea.toggleTranscribing); }); it("should throw error if request doesn't work", async () => { @@ -887,7 +880,7 @@ describe('plugin-meetings', () => { describe('#setCaptionLanguage', () => { beforeEach(() => { meeting.isTranscriptionSupported = sinon.stub(); - meeting.transcription = { languageOptions: {} }; + meeting.transcription = {languageOptions: {}}; webex.internal.voicea.on = sinon.stub(); webex.internal.voicea.off = sinon.stub(); webex.internal.voicea.setCaptionLanguage = sinon.stub(); @@ -913,23 +906,23 @@ describe('plugin-meetings', () => { const languageCode = 'fr'; meeting.setCaptionLanguage(languageCode).then((resolvedLanguageCode) => { - assert.calledWith( - webex.internal.voicea.requestLanguage, + assert.calledWith(webex.internal.voicea.requestLanguage, languageCode); + assert.equal(resolvedLanguageCode, languageCode); + assert.equal( + meeting.transcription.languageOptions.currentCaptionLanguage, languageCode ); - assert.equal(resolvedLanguageCode, languageCode); - assert.equal(meeting.transcription.languageOptions.currentCaptionLanguage, languageCode); done(); }); assert.calledOnceWithMatch( webex.internal.voicea.on, - VOICEAEVENTS.CAPTION_LANGUAGE_UPDATE, + VOICEAEVENTS.CAPTION_LANGUAGE_UPDATE ); // Trigger the event const voiceaListenerLangugeUpdate = webex.internal.voicea.on.getCall(0).args[1]; - voiceaListenerLangugeUpdate({ statusCode: 200, languageCode }); + voiceaListenerLangugeUpdate({statusCode: 200, languageCode}); }); it('should reject if the statusCode in payload is not 200', (done) => { @@ -937,8 +930,8 @@ describe('plugin-meetings', () => { const languageCode = 'fr'; const rejectPayload = { statusCode: 400, - message: 'some error message' - } + message: 'some error message', + }; meeting.setCaptionLanguage(languageCode).catch((payload) => { assert.equal(payload, rejectPayload); @@ -947,20 +940,19 @@ describe('plugin-meetings', () => { assert.calledOnceWithMatch( webex.internal.voicea.on, - VOICEAEVENTS.CAPTION_LANGUAGE_UPDATE, + VOICEAEVENTS.CAPTION_LANGUAGE_UPDATE ); // Trigger the event const voiceaListenerLangugeUpdate = webex.internal.voicea.on.getCall(0).args[1]; voiceaListenerLangugeUpdate(rejectPayload); }); - }); describe('#setSpokenLanguage', () => { beforeEach(() => { meeting.isTranscriptionSupported = sinon.stub(); - meeting.transcription = { languageOptions: {} }; + meeting.transcription = {languageOptions: {}}; webex.internal.voicea.on = sinon.stub(); webex.internal.voicea.off = sinon.stub(); webex.internal.voicea.setSpokenLanguage = sinon.stub(); @@ -985,47 +977,37 @@ describe('plugin-meetings', () => { const languageCode = 'fr'; meeting.setSpokenLanguage(languageCode).then((resolvedLanguageCode) => { - assert.calledWith( - webex.internal.voicea.setSpokenLanguage, - languageCode - ); + assert.calledWith(webex.internal.voicea.setSpokenLanguage, languageCode); assert.equal(resolvedLanguageCode, languageCode); assert.equal(meeting.transcription.languageOptions.currentSpokenLanguage, languageCode); done(); }); - assert.calledOnceWithMatch( - webex.internal.voicea.on, - VOICEAEVENTS.SPOKEN_LANGUAGE_UPDATE, - ); + assert.calledOnceWithMatch(webex.internal.voicea.on, VOICEAEVENTS.SPOKEN_LANGUAGE_UPDATE); // Trigger the event const voiceaListenerLangugeUpdate = webex.internal.voicea.on.getCall(0).args[1]; - voiceaListenerLangugeUpdate({ languageCode }); + voiceaListenerLangugeUpdate({languageCode}); }); it('should reject if the language code does not exist in payload', (done) => { meeting.isTranscriptionSupported.returns(true); const languageCode = 'fr'; const rejectPayload = { - 'message': 'some error message' - } + message: 'some error message', + }; meeting.setSpokenLanguage(languageCode).catch((payload) => { assert.equal(payload, rejectPayload); done(); }); - assert.calledOnceWithMatch( - webex.internal.voicea.on, - VOICEAEVENTS.SPOKEN_LANGUAGE_UPDATE, - ); + assert.calledOnceWithMatch(webex.internal.voicea.on, VOICEAEVENTS.SPOKEN_LANGUAGE_UPDATE); // Trigger the event const voiceaListenerLangugeUpdate = webex.internal.voicea.on.getCall(0).args[1]; voiceaListenerLangugeUpdate(rejectPayload); }); - }); describe('transcription events', () => { @@ -1269,7 +1251,7 @@ describe('plugin-meetings', () => { file: 'meeting/index', function: 'join', }, - EVENT_TRIGGERS.MEETING_TRANSCRIPTION_CONNECTED, + EVENT_TRIGGERS.MEETING_TRANSCRIPTION_CONNECTED ); }); @@ -2640,6 +2622,7 @@ describe('plugin-meetings', () => { beforeEach(async () => { meeting.meetingState = 'ACTIVE'; + meeting.remoteShareInstanceId = '1234'; prevConfigValue = meeting.config.stats.enableStatsAnalyzer; meeting.config.stats.enableStatsAnalyzer = true; @@ -2745,6 +2728,66 @@ describe('plugin-meetings', () => { }); }); + it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and sends metrics for share', async () => { + statsAnalyzerStub.emit( + {file: 'test', function: 'test'}, + StatsAnalyzerModule.EVENTS.REMOTE_MEDIA_STARTED, + {type: 'share'} + ); + + assert.calledWith( + TriggerProxy.trigger, + sinon.match.instanceOf(Meeting), + { + file: 'meeting/index', + function: 'addMedia', + }, + EVENT_TRIGGERS.MEETING_MEDIA_REMOTE_STARTED, + { + type: 'share', + } + ); + assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, { + name: 'client.media.rx.start', + payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId}, + options: { + meetingId: meeting.id, + }, + }); + + assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, { + name: 'client.media.render.start', + payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId}, + options: { + meetingId: meeting.id, + }, + }); + }); + + it('REMOTE_MEDIA_STOPPED triggers the right metrics for share', async () => { + statsAnalyzerStub.emit( + {file: 'test', function: 'test'}, + StatsAnalyzerModule.EVENTS.REMOTE_MEDIA_STOPPED, + {type: 'share'} + ); + + assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, { + name: 'client.media.rx.stop', + payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId}, + options: { + meetingId: meeting.id, + }, + }); + + assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, { + name: 'client.media.render.stop', + payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId}, + options: { + meetingId: meeting.id, + }, + }); + }); + it('calls submitMQE correctly', async () => { const fakeData = {intervalMetadata: {bla: 'bla'}}; @@ -2989,9 +3032,10 @@ describe('plugin-meetings', () => { meeting.setMercuryListener = sinon.stub(); meeting.locusInfo.onFullLocus = sinon.stub(); meeting.webex.meetings.geoHintInfo = {regionCode: 'EU', countryCode: 'UK'}; - meeting.roap.doTurnDiscovery = sinon - .stub() - .resolves({turnServerInfo: { url: 'turn-url', username: 'turn user', password: 'turn password'}, turnDiscoverySkippedReason: 'reachability'}); + meeting.roap.doTurnDiscovery = sinon.stub().resolves({ + turnServerInfo: {url: 'turn-url', username: 'turn user', password: 'turn password'}, + turnDiscoverySkippedReason: 'reachability', + }); meeting.deferSDPAnswer = new Defer(); meeting.deferSDPAnswer.resolve(); meeting.webex.meetings.meetingCollection = new MeetingCollection(); @@ -3268,7 +3312,9 @@ describe('plugin-meetings', () => { switch (type) { case 'audio': if (stream?.readyState === 'ended') { - assert.notCalled(meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream); + assert.notCalled( + meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream + ); } else { assert.calledOnceWithExactly( meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream, @@ -6417,7 +6463,7 @@ describe('plugin-meetings', () => { [ { - endedStream: 'microphone', + endedStream: 'microphone', streams: { microphone: { readyState: 'ended', @@ -6430,7 +6476,7 @@ describe('plugin-meetings', () => { }, }, { - endedStream: 'camera', + endedStream: 'camera', streams: { microphone: undefined, camera: { @@ -6443,7 +6489,7 @@ describe('plugin-meetings', () => { }, }, { - endedStream: 'screenShare audio', + endedStream: 'screenShare audio', streams: { microphone: undefined, camera: undefined, @@ -6456,7 +6502,7 @@ describe('plugin-meetings', () => { }, }, { - endedStream: 'screenShare video', + endedStream: 'screenShare video', streams: { microphone: undefined, camera: undefined, @@ -6471,7 +6517,7 @@ describe('plugin-meetings', () => { ].forEach(({endedStream, streams}) => { it(`throws error if readyState of ${endedStream} is ended`, async () => { assert.isRejected(meeting.publishStreams(streams)); - }) + }); }); }); @@ -9794,9 +9840,16 @@ describe('plugin-meetings', () => { it('check triggerAnnotationInfoEvent event', () => { TriggerProxy.trigger.reset(); const annotationInfo = {version: '1', policy: 'Approval'}; - const expectAnnotationInfo = {annotationInfo, meetingId: meeting.id, resourceType: 'FILE'}; + const expectAnnotationInfo = { + annotationInfo, + meetingId: meeting.id, + resourceType: 'FILE', + }; meeting.webex.meetings = {}; - meeting.triggerAnnotationInfoEvent({annotation: annotationInfo, resourceType: 'FILE'}, {}); + meeting.triggerAnnotationInfoEvent( + {annotation: annotationInfo, resourceType: 'FILE'}, + {} + ); assert.calledWith( TriggerProxy.trigger, {}, @@ -9838,7 +9891,10 @@ describe('plugin-meetings', () => { ); TriggerProxy.trigger.reset(); - meeting.triggerAnnotationInfoEvent(null, {annotation: annotationInfoUpdate, resourceType: 'FILE'}); + meeting.triggerAnnotationInfoEvent(null, { + annotation: annotationInfoUpdate, + resourceType: 'FILE', + }); assert.notCalled(TriggerProxy.trigger); }); }); @@ -9863,10 +9919,8 @@ describe('plugin-meetings', () => { }; const SHARE_TYPE = { - FILE: - 'FILE', - DESKTOP: - 'DESKTOP', + FILE: 'FILE', + DESKTOP: 'DESKTOP', }; const DEVICE_URL = { @@ -9881,7 +9935,7 @@ describe('plugin-meetings', () => { disposition = null, deviceUrlSharing = null, annotation = undefined, - resourceType = undefined, + resourceType = undefined ) => ({ beneficiaryId, disposition, @@ -9942,7 +9996,7 @@ describe('plugin-meetings', () => { FLOOR_ACTION.GRANTED, deviceUrlSharing, annotation, - resourceType + resourceType ); if (isEqual(newPayload.current, newPayload.previous)) { @@ -10003,7 +10057,7 @@ describe('plugin-meetings', () => { url, shareInstanceId, annotationInfo: undefined, - resourceType: undefined + resourceType: undefined, }, }); } @@ -10871,31 +10925,30 @@ describe('plugin-meetings', () => { describe('File Share --> Desktop Share', () => { it('Scenario #1: remote person A shares file then share desktop', () => { const data1 = generateData( - blankPayload, - true, - true, - USER_IDS.ME, - undefined, - false, - undefined, - undefined, - undefined, - undefined, - DEVICE_URL.LOCAL_WEB, - SHARE_TYPE.FILE + blankPayload, + true, + true, + USER_IDS.ME, + undefined, + false, + undefined, + undefined, + undefined, + undefined, + DEVICE_URL.LOCAL_WEB, + SHARE_TYPE.FILE ); const data2 = generateData( - data1.payload, - true, - false, - USER_IDS.ME, - SHARE_TYPE.DESKTOP + data1.payload, + true, + false, + USER_IDS.ME, + SHARE_TYPE.DESKTOP ); const data3 = generateData(data2.payload, true, true, USER_IDS.ME); payloadTestHelper([data1, data2, data3]); }); - }); }); }); diff --git a/packages/@webex/plugin-meetings/test/unit/spec/meetings/index.js b/packages/@webex/plugin-meetings/test/unit/spec/meetings/index.js index d6c9a9d6c77..acf846d6340 100644 --- a/packages/@webex/plugin-meetings/test/unit/spec/meetings/index.js +++ b/packages/@webex/plugin-meetings/test/unit/spec/meetings/index.js @@ -412,9 +412,13 @@ describe('plugin-meetings', () => { assert.exists(result); assert.instanceOf(result, NoiseReductionEffect); assert.containsAllKeys(result, ['audioContext', 'isEnabled', 'isReady', 'options']); + assert.equal(result.options.authToken, 'fake_token'); assert.deepEqual(result.options, { - authToken: 'fake_token', audioContext: {}, + authToken: 'fake_token', + mode: 'WORKLET', + env: 'prod', + avoidSimd: false, }); assert.exists(result.enable); assert.exists(result.disable); @@ -424,8 +428,9 @@ describe('plugin-meetings', () => { it('creates noise reduction effect with custom options passed', async () => { const effectOptions = { audioContext: {}, - mode: 'WORKLET', - env: 'prod', + mode: 'LEGACY', + env: 'int', + avoidSimd: true, }; const result = await webex.meetings.createNoiseReductionEffect(effectOptions); @@ -2003,24 +2008,27 @@ describe('plugin-meetings', () => { assert.notCalled(loggerProxySpy); }); - forEach([ - {user: undefined}, - {user: {userPreferences: {}}}, - {user: {userPreferences: {userPreferencesItems: {}}}}, - {user: {userPreferences: {userPreferencesItems: {preferredWebExSite: undefined}}}}, - ], ({user}) => { - it(`should handle invalid user data ${user}`, async () => { - setup({user}); + forEach( + [ + {user: undefined}, + {user: {userPreferences: {}}}, + {user: {userPreferences: {userPreferencesItems: {}}}}, + {user: {userPreferences: {userPreferencesItems: {preferredWebExSite: undefined}}}}, + ], + ({user}) => { + it(`should handle invalid user data ${user}`, async () => { + setup({user}); - await webex.meetings.fetchUserPreferredWebexSite(); + await webex.meetings.fetchUserPreferredWebexSite(); - assert.equal(webex.meetings.preferredWebexSite, ''); - assert.calledOnceWithExactly( - loggerProxySpy, - 'Failed to fetch preferred site from user - no site will be set' - ); - }); - }); + assert.equal(webex.meetings.preferredWebexSite, ''); + assert.calledOnceWithExactly( + loggerProxySpy, + 'Failed to fetch preferred site from user - no site will be set' + ); + }); + } + ); it('should handle a get user failure', async () => { setup(); @@ -2035,7 +2043,6 @@ describe('plugin-meetings', () => { 'Failed to fetch preferred site from user - no site will be set' ); }); - }); }); diff --git a/yarn.lock b/yarn.lock index 0a1ecb24d9f..7a25db00ef7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7029,9 +7029,9 @@ __metadata: languageName: unknown linkType: soft -"@webex/event-dictionary-ts@npm:^1.0.1401": - version: 1.0.1401 - resolution: "@webex/event-dictionary-ts@npm:1.0.1401" +"@webex/event-dictionary-ts@npm:^1.0.1406": + version: 1.0.1406 + resolution: "@webex/event-dictionary-ts@npm:1.0.1406" dependencies: amf-client-js: "npm:^5.2.6" json-schema-to-typescript: "npm:^12.0.0" @@ -7039,7 +7039,7 @@ __metadata: ramldt2jsonschema: "npm:^1.2.3" shelljs: "npm:^0.8.5" webapi-parser: "npm:^0.5.0" - checksum: c4d415d7aa1f9cf3f63e2b0c03e3d49978151b13c5cf253942b122eeb11759257ca3de67515e9e23868df2ca6e0ac2347e9a11990a37e24cb97f0734d403ff4b + checksum: 2188a6368758001a0d839e934f4131100a40f16911a931abf0be61e7a98b001c4fe2c3108b7bd657f6d1f768c3c4aedeea9480296939ca4a7837db264df2a589 languageName: node linkType: hard @@ -7552,7 +7552,7 @@ __metadata: "@webex/common": "workspace:*" "@webex/common-timers": "workspace:*" "@webex/eslint-config-legacy": "workspace:*" - "@webex/event-dictionary-ts": ^1.0.1401 + "@webex/event-dictionary-ts": ^1.0.1406 "@webex/internal-plugin-device": "workspace:*" "@webex/internal-plugin-metrics": "workspace:*" "@webex/jest-config-legacy": "workspace:*" @@ -7835,6 +7835,15 @@ __metadata: languageName: node linkType: hard +"@webex/ladon-ts@npm:^4.3.0": + version: 4.3.0 + resolution: "@webex/ladon-ts@npm:4.3.0" + dependencies: + onnxruntime-web: "npm:^1.15.1" + checksum: d606e2d4d2875380ba58bc29f31a26b126a49c8d6a9ad807b6274ee91704043853078f1009a78639becc096c4b408370b23b2028ecf537d592ee0aaeacbd0e18 + languageName: node + linkType: hard + "@webex/legacy-tools@workspace:*, @webex/legacy-tools@workspace:packages/legacy/tools": version: 0.0.0-use.local resolution: "@webex/legacy-tools@workspace:packages/legacy/tools" @@ -7917,7 +7926,7 @@ __metadata: "@webex/test-helper-chai": "workspace:*" "@webex/test-helper-mock-webex": "workspace:*" "@webex/ts-events": ^1.1.0 - "@webex/web-media-effects": ^2.15.6 + "@webex/web-media-effects": 2.18.0 eslint: ^8.24.0 jsdom-global: 3.0.2 sinon: ^9.2.4 @@ -8886,6 +8895,19 @@ __metadata: languageName: node linkType: hard +"@webex/web-media-effects@npm:2.18.0": + version: 2.18.0 + resolution: "@webex/web-media-effects@npm:2.18.0" + dependencies: + "@webex/ladon-ts": "npm:^4.3.0" + events: "npm:^3.3.0" + js-logger: "npm:^1.6.1" + typed-emitter: "npm:^1.4.0" + uuid: "npm:^9.0.1" + checksum: d2b4bcdcc2af87c0bed9c753fa06a34ac663e703c7a1e0bbf542558e23667b9a15b3c4ca594c9b4c8af1a28445f0afd7b4e622c411b0a8c567c84997f2fe44c2 + languageName: node + linkType: hard + "@webex/web-media-effects@npm:^2.15.6": version: 2.15.6 resolution: "@webex/web-media-effects@npm:2.15.6"