Skip to content

Commit

Permalink
feat(MeetingSdkAdapter): implement settings control in its own file a…
Browse files Browse the repository at this point in the history
…nd create the tests accordingly
  • Loading branch information
karinasigartau0798 authored and cipak committed Jul 22, 2021
1 parent 8f0e9b4 commit 9d085a2
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 73 deletions.
82 changes: 9 additions & 73 deletions src/MeetingsSDKAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import {
fromEvent,
merge,
Observable,
BehaviorSubject,
throwError,
defer,
of,
} from 'rxjs';
Expand All @@ -26,6 +24,7 @@ import {
} from 'rxjs/operators';

import RosterControl from './MeetingsSDKAdapter/controls/RosterControl';
import SettingsControl from './MeetingsSDKAdapter/controls/SettingsControl';

// TODO: Figure out how to import JS Doc definitions and remove duplication.
/**
Expand Down Expand Up @@ -56,7 +55,6 @@ const EVENT_REMOTE_SHARE_STOP = 'meeting:stoppedSharingRemote';
// Adapter Events
const EVENT_MEETING_UPDATED = 'adapter:meeting:updated';
const EVENT_MEDIA_LOCAL_UPDATE = 'adapter:media:local:update';
const EVENT_SETTINGS_TOGGLE = 'adapter:settings:toggle';
const EVENT_CAMERA_SWITCH = 'adapter:camera:switch';
const EVENT_MICROPHONE_SWITCH = 'adapter:microphone:switch';
const EVENT_SPEAKER_SWITCH = 'adapter:speaker:switch';
Expand Down Expand Up @@ -143,12 +141,7 @@ export default class MeetingsSDKAdapter extends MeetingsAdapter {
};

this.meetingControls[ROSTER_CONTROL] = new RosterControl(this, ROSTER_CONTROL);

this.meetingControls[SETTINGS_CONTROL] = {
ID: SETTINGS_CONTROL,
action: this.toggleSettings.bind(this),
display: this.settingsControl.bind(this),
};
this.meetingControls[SETTINGS_CONTROL] = new SettingsControl(this, SETTINGS_CONTROL);

this.meetingControls[SWITCH_CAMERA_CONTROL] = {
ID: SWITCH_CAMERA_CONTROL,
Expand Down Expand Up @@ -517,7 +510,7 @@ export default class MeetingsSDKAdapter extends MeetingsAdapter {
const sdkMeeting = this.fetchMeeting(meeting.ID);

this.meetings[meeting.ID] = meeting;
sdkMeeting.emit(EVENT_MEETING_UPDATED);
sdkMeeting.emit(EVENT_MEETING_UPDATED, meeting);
}),
catchError((err) => {
// eslint-disable-next-line no-console
Expand Down Expand Up @@ -994,7 +987,6 @@ export default class MeetingsSDKAdapter extends MeetingsAdapter {
* Attempts to toggle roster to the given meeting ID.
* A roster toggle event is dispatched.
*
* @private
* @param {string} ID ID of the meeting to toggle roster
*/
toggleRoster(ID) {
Expand All @@ -1005,64 +997,10 @@ export default class MeetingsSDKAdapter extends MeetingsAdapter {
* Toggles the showSettings flag of the given meeting ID.
* A settings toggle event is dispatched.
*
* @private
* @param {string} ID Meeting ID
*/
toggleSettings(ID) {
const sdkMeeting = this.fetchMeeting(ID);
const showSettings = !this.meetings[ID].showSettings;

this.meetings[ID].showSettings = showSettings;

sdkMeeting.emit(EVENT_SETTINGS_TOGGLE, {
state: showSettings
? MeetingControlState.ACTIVE
: MeetingControlState.INACTIVE,
});
}

/**
* Returns an observable that emits the display data of a settings control.
*
* @private
* @param {string} ID Meeting id
* @returns {Observable.<MeetingControlDisplay>} Observable stream that emits display data of the settings control
*/
settingsControl(ID) {
const sdkMeeting = this.fetchMeeting(ID);
const active = {
ID: SETTINGS_CONTROL,
icon: 'settings_32',
tooltip: 'Hide settings panel',
state: MeetingControlState.ACTIVE,
text: 'Settings',
};
const inactive = {
ID: SETTINGS_CONTROL,
icon: 'settings_32',
tooltip: 'Show settings panel',
state: MeetingControlState.INACTIVE,
text: 'Settings',
};

let state$;

if (sdkMeeting) {
const initialState = (this.meetings[ID] && this.meetings[ID].showSettings)
? active
: inactive;

state$ = new BehaviorSubject(initialState);

const settingsEvent$ = fromEvent(sdkMeeting, EVENT_SETTINGS_TOGGLE)
.pipe(map(({state}) => (state === MeetingControlState.ACTIVE ? active : inactive)));

settingsEvent$.subscribe((value) => state$.next(value));
} else {
state$ = throwError(new Error(`Could not find meeting with ID "${ID}" to add settings control`));
}

return state$;
return this.updateMeeting(ID, ({showSettings}) => ({showSettings: !showSettings}));
}

/**
Expand Down Expand Up @@ -1361,7 +1299,11 @@ export default class MeetingsSDKAdapter extends MeetingsAdapter {
observer.complete();
});

const meetingUpdateEvent$ = fromEvent(sdkMeeting, EVENT_MEETING_UPDATED);
const meetingUpdateEvent$ = fromEvent(sdkMeeting, EVENT_MEETING_UPDATED).pipe(
tap((meeting) => {
this.meetings[ID] = meeting;
}),
);

const meetingWithMediaReadyEvent$ = fromEvent(sdkMeeting, EVENT_MEDIA_READY).pipe(
filter((event) => MEDIA_EVENT_TYPES.includes(event.type)),
Expand All @@ -1385,12 +1327,8 @@ export default class MeetingsSDKAdapter extends MeetingsAdapter {

const meetingWithLocalUpdateEvent$ = fromEvent(sdkMeeting, EVENT_MEDIA_LOCAL_UPDATE);

const meetingWithUpdateEvent$ = fromEvent(sdkMeeting, EVENT_MEETING_UPDATED);

const meetingWithSwitchSpeakerEvent$ = fromEvent(sdkMeeting, EVENT_SPEAKER_SWITCH);

const meetingWithSettingsToggleEvent$ = fromEvent(sdkMeeting, EVENT_SETTINGS_TOGGLE);

const meetingWithSwitchCameraEvent$ = fromEvent(sdkMeeting, EVENT_CAMERA_SWITCH);

const meetingWithSwitchMicrophoneEvent$ = fromEvent(sdkMeeting, EVENT_MICROPHONE_SWITCH);
Expand Down Expand Up @@ -1420,8 +1358,6 @@ export default class MeetingsSDKAdapter extends MeetingsAdapter {
meetingWithLocalShareStoppedEvent$,
meetingWithMediaShareEvent$,
meetingWithMediaStoppedShareEvent$,
meetingWithUpdateEvent$,
meetingWithSettingsToggleEvent$,
meetingStateChange$,
meetingWithSwitchCameraEvent$,
meetingWithSwitchMicrophoneEvent$,
Expand Down
26 changes: 26 additions & 0 deletions src/MeetingsSDKAdapter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe('Meetings SDK Adapter', () => {
remoteVideo: null,
remoteShare: null,
showRoster: null,
showSettings: false,
title: 'my meeting',
cameraID: null,
microphoneID: null,
Expand Down Expand Up @@ -882,6 +883,31 @@ describe('Meetings SDK Adapter', () => {
});
});

describe('toggleSettings()', () => {
test('shows settings if settings is hidden', async () => {
meetingSDKAdapter.meetings[meetingID].showSettings = false;
await meetingSDKAdapter.toggleSettings(meetingID);
expect(mockSDKMeeting.emit).toHaveBeenCalledTimes(1);
expect(mockSDKMeeting.emit.mock.calls[0][0]).toBe('adapter:meeting:updated');
expect(mockSDKMeeting.emit.mock.calls[0][1]).toMatchObject({showSettings: true});
});

test('hides settings if settings is shown', async () => {
meetingSDKAdapter.meetings[meetingID].showSettings = true;
await meetingSDKAdapter.toggleSettings(meetingID);
expect(mockSDKMeeting.emit).toHaveBeenCalledTimes(1);
expect(mockSDKMeeting.emit.mock.calls[0][0]).toBe('adapter:meeting:updated');
expect(mockSDKMeeting.emit.mock.calls[0][1]).toMatchObject({showSettings: false});
});

test('returns a rejected promise if meeting does not exist', (done) => {
meetingSDKAdapter.toggleSettings('inexistent').catch((error) => {
expect(error.message).toBe('Could not find meeting with ID "inexistent"');
done();
});
});
});

describe('switchCameraControl()', () => {
test('returns the display data of a meeting control in a proper shape', (done) => {
meetingSDKAdapter.switchCameraControl(meetingID)
Expand Down
51 changes: 51 additions & 0 deletions src/MeetingsSDKAdapter/controls/SettingsControl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {Observable} from 'rxjs';
import {map, distinctUntilChanged} from 'rxjs/operators';
import {MeetingControlState} from '@webex/component-adapter-interfaces';
import MeetingControl from './MeetingControl';

/**
* Display options of a meeting control.
*
* @external MeetingControlDisplay
* @see {@link https://github.com/webex/component-adapter-interfaces/blob/master/src/MeetingsAdapter.js#L58}
*/

export default class SettingsControl extends MeetingControl {
/**
* Toggles the showSettings flag of the given meeting ID.
* A settings toggle event is dispatched.
*
* @param {string} meetingID Meeting ID
*/
action(meetingID) {
this.adapter.toggleSettings(meetingID);
}

/**
* Returns an observable that emits the display data of a settings control.
*
* @param {string} meetingID Meeting id
* @returns {Observable.<MeetingControlDisplay>} Observable stream that emits display data of the settings control
*/
display(meetingID) {
const active = {
ID: this.ID,
icon: 'settings_32',
tooltip: 'Hide settings panel',
state: MeetingControlState.ACTIVE,
text: 'Settings',
};
const inactive = {
ID: this.ID,
icon: 'settings_32',
tooltip: 'Show settings panel',
state: MeetingControlState.INACTIVE,
text: 'Settings',
};

return this.adapter.getMeeting(meetingID).pipe(
map(({showSettings}) => (showSettings ? active : inactive)),
distinctUntilChanged(),
);
}
}
71 changes: 71 additions & 0 deletions src/MeetingsSDKAdapter/controls/SettingsControl.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {first} from 'rxjs/operators';
import MeetingsSDKAdapter from '../../MeetingsSDKAdapter';
import createMockSDK from '../../mockSdk';

describe('Settings Control', () => {
let meeting;
let meetingID;
let meetingsSDKAdapter;
let mockSDK;

beforeEach(() => {
mockSDK = createMockSDK();
meetingID = 'meetingID';
meetingsSDKAdapter = new MeetingsSDKAdapter(mockSDK);
meeting = {
ID: meetingID,
localAudio: {
stream: null,
permission: null,
},
localShare: {
stream: null,
},
localVideo: {
stream: null,
permission: null,
},
remoteAudio: null,
remoteVideo: null,
remoteShare: null,
showRoster: null,
showSettings: false,
title: 'my meeting',
cameraID: null,
microphoneID: null,
speakerID: null,
};
meetingsSDKAdapter.meetings[meetingID] = meeting;
});

afterEach(() => {
meeting = null;
mockSDK = null;
meetingsSDKAdapter = null;
meetingID = null;
});

describe('display()', () => {
test('returns the display data of a meeting control in a proper shape', () => {
meetingsSDKAdapter.meetingControls.settings.display(meetingID).pipe(first())
.subscribe((dataDisplay) => {
expect(dataDisplay).toMatchObject({
ID: 'settings',
icon: 'settings_32',
tooltip: 'Show settings panel',
state: 'inactive',
text: 'Settings',
});
});
});
});

describe('action()', () => {
test('calls toggleSettings() SDK adapter method', async () => {
meetingsSDKAdapter.toggleSettings = jest.fn();
await meetingsSDKAdapter.meetingControls.settings.action(meetingID);
expect(meetingsSDKAdapter.toggleSettings).toHaveBeenCalledTimes(1);
expect(meetingsSDKAdapter.toggleSettings).toHaveBeenCalledWith(meetingID);
});
});
});
1 change: 1 addition & 0 deletions src/MeetingsSDKAdapter/controls/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable import/prefer-default-export */
export {default as MeetingControl} from './MeetingControl';
export {default as RosterControl} from './RosterControl';
export {default as SettingsControl} from './SettingsControl';

0 comments on commit 9d085a2

Please sign in to comment.