From c1c593514bb311fa32e7add20317c64594b0e41d Mon Sep 17 00:00:00 2001 From: Tim Mendoza Date: Thu, 3 Sep 2020 19:49:17 -0600 Subject: [PATCH 01/23] Fix networkQualityTests --- .../NetworkQualityLevel.test.tsx | 9 ++++-- .../NetworkQualityLevel.test.tsx.snap | 30 +++++++++---------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/components/NewtorkQualityLevel/NetworkQualityLevel.test.tsx b/src/components/NewtorkQualityLevel/NetworkQualityLevel.test.tsx index 084a9c7bc..e43dc1494 100644 --- a/src/components/NewtorkQualityLevel/NetworkQualityLevel.test.tsx +++ b/src/components/NewtorkQualityLevel/NetworkQualityLevel.test.tsx @@ -4,17 +4,20 @@ import NetworkQualityLevel from './NetworkQualityLevel'; describe('the NetworkQualityLevel component', () => { it('should render correctly for level 5', () => { - const tree = renderer.create().toJSON(); + const mockParticipant = { networkQualityLevel: 5, on: () => {} } as any; + const tree = renderer.create().toJSON(); expect(tree).toMatchSnapshot(); }); it('should render correctly for level 3', () => { - const tree = renderer.create().toJSON(); + const mockParticipant = { networkQualityLevel: 3, on: () => {} } as any; + const tree = renderer.create().toJSON(); expect(tree).toMatchSnapshot(); }); it('should render correctly for level 0', () => { - const tree = renderer.create().toJSON(); + const mockParticipant = { networkQualityLevel: 0, on: () => {} } as any; + const tree = renderer.create().toJSON(); expect(tree).toMatchSnapshot(); }); }); diff --git a/src/components/NewtorkQualityLevel/__snapshots__/NetworkQualityLevel.test.tsx.snap b/src/components/NewtorkQualityLevel/__snapshots__/NetworkQualityLevel.test.tsx.snap index 6ea682346..10193ae7f 100644 --- a/src/components/NewtorkQualityLevel/__snapshots__/NetworkQualityLevel.test.tsx.snap +++ b/src/components/NewtorkQualityLevel/__snapshots__/NetworkQualityLevel.test.tsx.snap @@ -7,7 +7,7 @@ exports[`the NetworkQualityLevel component should render correctly for level 0 1
Date: Thu, 3 Sep 2020 19:50:47 -0600 Subject: [PATCH 02/23] Rename NewtorkQualityLevel to NetworkQualityLevel --- .../NetworkQualityLevel.test.tsx | 0 .../NetworkQualityLevel.tsx | 0 .../__snapshots__/NetworkQualityLevel.test.tsx.snap | 0 src/components/ParticipantInfo/ParticipantInfo.tsx | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) rename src/components/{NewtorkQualityLevel => NetworkQualityLevel}/NetworkQualityLevel.test.tsx (100%) rename src/components/{NewtorkQualityLevel => NetworkQualityLevel}/NetworkQualityLevel.tsx (100%) rename src/components/{NewtorkQualityLevel => NetworkQualityLevel}/__snapshots__/NetworkQualityLevel.test.tsx.snap (100%) diff --git a/src/components/NewtorkQualityLevel/NetworkQualityLevel.test.tsx b/src/components/NetworkQualityLevel/NetworkQualityLevel.test.tsx similarity index 100% rename from src/components/NewtorkQualityLevel/NetworkQualityLevel.test.tsx rename to src/components/NetworkQualityLevel/NetworkQualityLevel.test.tsx diff --git a/src/components/NewtorkQualityLevel/NetworkQualityLevel.tsx b/src/components/NetworkQualityLevel/NetworkQualityLevel.tsx similarity index 100% rename from src/components/NewtorkQualityLevel/NetworkQualityLevel.tsx rename to src/components/NetworkQualityLevel/NetworkQualityLevel.tsx diff --git a/src/components/NewtorkQualityLevel/__snapshots__/NetworkQualityLevel.test.tsx.snap b/src/components/NetworkQualityLevel/__snapshots__/NetworkQualityLevel.test.tsx.snap similarity index 100% rename from src/components/NewtorkQualityLevel/__snapshots__/NetworkQualityLevel.test.tsx.snap rename to src/components/NetworkQualityLevel/__snapshots__/NetworkQualityLevel.test.tsx.snap diff --git a/src/components/ParticipantInfo/ParticipantInfo.tsx b/src/components/ParticipantInfo/ParticipantInfo.tsx index c409019d2..b84e21c82 100644 --- a/src/components/ParticipantInfo/ParticipantInfo.tsx +++ b/src/components/ParticipantInfo/ParticipantInfo.tsx @@ -6,7 +6,7 @@ import { LocalAudioTrack, LocalVideoTrack, Participant, RemoteAudioTrack, Remote import AudioLevelIndicator from '../AudioLevelIndicator/AudioLevelIndicator'; import { Avatar } from '../../icons/Avatar'; import BandwidthWarning from '../BandwidthWarning/BandwidthWarning'; -import NetworkQualityLevel from '../NewtorkQualityLevel/NetworkQualityLevel'; +import NetworkQualityLevel from '../NetworkQualityLevel/NetworkQualityLevel'; import PinIcon from './PinIcon/PinIcon'; import Typography from '@material-ui/core/Typography'; From ed8caf9ea527b831c6c27b59a0fcacdf318c47d5 Mon Sep 17 00:00:00 2001 From: Tim Mendoza Date: Thu, 3 Sep 2020 20:49:30 -0600 Subject: [PATCH 03/23] Pass isLocalParticipant as prop instead of using useVideoContext --- src/components/Participant/Participant.tsx | 3 +++ src/components/ParticipantInfo/ParticipantInfo.tsx | 8 ++------ .../ParticipantTracks/ParticipantTracks.tsx | 7 +++---- src/components/Publication/Publication.tsx | 11 ++++++++--- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/components/Participant/Participant.tsx b/src/components/Participant/Participant.tsx index 820aca00a..2aa8d0c2d 100644 --- a/src/components/Participant/Participant.tsx +++ b/src/components/Participant/Participant.tsx @@ -10,6 +10,7 @@ interface ParticipantProps { onClick: () => void; isSelected: boolean; isDominantSpeaker?: boolean; + isLocalParticipant?: boolean; } export default function Participant({ @@ -19,6 +20,7 @@ export default function Participant({ onClick, isSelected, isDominantSpeaker, + isLocalParticipant, }: ParticipantProps) { return ( diff --git a/src/components/ParticipantInfo/ParticipantInfo.tsx b/src/components/ParticipantInfo/ParticipantInfo.tsx index b84e21c82..b989dfb9a 100644 --- a/src/components/ParticipantInfo/ParticipantInfo.tsx +++ b/src/components/ParticipantInfo/ParticipantInfo.tsx @@ -13,7 +13,6 @@ import Typography from '@material-ui/core/Typography'; import useIsTrackSwitchedOff from '../../hooks/useIsTrackSwitchedOff/useIsTrackSwitchedOff'; import usePublications from '../../hooks/usePublications/usePublications'; import useTrack from '../../hooks/useTrack/useTrack'; -import useVideoContext from '../../hooks/useVideoContext/useVideoContext'; const BORDER_SIZE = 2; @@ -112,6 +111,7 @@ interface ParticipantInfoProps { onClick: () => void; isSelected: boolean; isDominantSpeaker?: boolean; + isLocalParticipant?: boolean; } export default function ParticipantInfo({ @@ -120,12 +120,8 @@ export default function ParticipantInfo({ isSelected, children, isDominantSpeaker, + isLocalParticipant, }: ParticipantInfoProps) { - const { - room: { localParticipant }, - } = useVideoContext(); - const isLocalParticipant = participant === localParticipant; - const publications = usePublications(participant); const audioPublication = publications.find(p => p.kind === 'audio'); diff --git a/src/components/ParticipantTracks/ParticipantTracks.tsx b/src/components/ParticipantTracks/ParticipantTracks.tsx index 7c9f45403..0d9855bbf 100644 --- a/src/components/ParticipantTracks/ParticipantTracks.tsx +++ b/src/components/ParticipantTracks/ParticipantTracks.tsx @@ -2,13 +2,13 @@ import React from 'react'; import { Participant, Track } from 'twilio-video'; import Publication from '../Publication/Publication'; import usePublications from '../../hooks/usePublications/usePublications'; -import useVideoContext from '../../hooks/useVideoContext/useVideoContext'; interface ParticipantTracksProps { participant: Participant; disableAudio?: boolean; enableScreenShare?: boolean; videoPriority?: Track.Priority | null; + isLocalParticipant?: boolean; } /* @@ -24,10 +24,9 @@ export default function ParticipantTracks({ disableAudio, enableScreenShare, videoPriority, + isLocalParticipant, }: ParticipantTracksProps) { - const { room } = useVideoContext(); const publications = usePublications(participant); - const isLocal = participant === room.localParticipant; let filteredPublications; @@ -44,7 +43,7 @@ export default function ParticipantTracks({ key={publication.kind} publication={publication} participant={participant} - isLocal={isLocal} + isLocalParticipant={isLocalParticipant} disableAudio={disableAudio} videoPriority={videoPriority} /> diff --git a/src/components/Publication/Publication.tsx b/src/components/Publication/Publication.tsx index a94befa91..c91c48996 100644 --- a/src/components/Publication/Publication.tsx +++ b/src/components/Publication/Publication.tsx @@ -15,12 +15,17 @@ import { interface PublicationProps { publication: LocalTrackPublication | RemoteTrackPublication; participant: Participant; - isLocal: boolean; + isLocalParticipant?: boolean; disableAudio?: boolean; videoPriority?: Track.Priority | null; } -export default function Publication({ publication, isLocal, disableAudio, videoPriority }: PublicationProps) { +export default function Publication({ + publication, + isLocalParticipant, + disableAudio, + videoPriority, +}: PublicationProps) { const track = useTrack(publication); if (!track) return null; @@ -31,7 +36,7 @@ export default function Publication({ publication, isLocal, disableAudio, videoP ); case 'audio': From 96a13d5fa0e11b3dfe15cf6651a67fa82286df38 Mon Sep 17 00:00:00 2001 From: Tim Mendoza Date: Thu, 3 Sep 2020 20:49:55 -0600 Subject: [PATCH 04/23] Add isLocalParticipant to local participant --- src/components/ParticipantList/ParticipantList.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ParticipantList/ParticipantList.tsx b/src/components/ParticipantList/ParticipantList.tsx index 4b30bd257..ef69cb963 100644 --- a/src/components/ParticipantList/ParticipantList.tsx +++ b/src/components/ParticipantList/ParticipantList.tsx @@ -55,6 +55,7 @@ export default function ParticipantList() { participant={localParticipant} isSelected={selectedParticipant === localParticipant} onClick={() => setSelectedParticipant(localParticipant)} + isLocalParticipant={true} /> {participants.map(participant => ( Date: Thu, 3 Sep 2020 20:50:06 -0600 Subject: [PATCH 05/23] Update ParticipantInfo tests --- .../ParticipantInfo/ParticipantInfo.test.tsx | 73 ++++++++++--------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/src/components/ParticipantInfo/ParticipantInfo.test.tsx b/src/components/ParticipantInfo/ParticipantInfo.test.tsx index 020ac570e..06ade891d 100644 --- a/src/components/ParticipantInfo/ParticipantInfo.test.tsx +++ b/src/components/ParticipantInfo/ParticipantInfo.test.tsx @@ -13,105 +13,108 @@ const mockUsePublications = usePublications as jest.Mock; const mockUseIsTrackSwitchedOff = useIsTrackSwitchedOff as jest.Mock; describe('the ParticipantInfo component', () => { - it('should display ScreenShare icon when participant has published a screen share track', () => { - mockUsePublications.mockImplementation(() => [{ trackName: 'screen' }]); - const wrapper = shallow( - {}} isSelected={false} participant={{ identity: 'mockIdentity' } as any}> - mock children - - ); - expect(wrapper.find('ScreenShareIcon').exists()).toEqual(true); - }); - - it('should not display ScreenShare icon when participant has not published a screen share track', () => { + it('should the Avatar component when no video tracks are published', () => { mockUsePublications.mockImplementation(() => []); const wrapper = shallow( {}} isSelected={false} participant={{ identity: 'mockIdentity' } as any}> mock children ); - expect(wrapper.find('ScreenShareIcon').exists()).toEqual(false); + expect(wrapper.find('.makeStyles-avatarContainer-6').exists()).toBe(true); }); - it('should add hideVideoProp to InfoContainer component when no video tracks are published', () => { - mockUsePublications.mockImplementation(() => []); + it('should not display the Avatar component when a video track is published', () => { + mockUsePublications.mockImplementation(() => [{ trackName: 'camera-123456' }]); const wrapper = shallow( {}} isSelected={false} participant={{ identity: 'mockIdentity' } as any}> mock children ); - expect(wrapper.find('.makeStyles-infoContainer-3').prop('className')).toContain('hideVideo'); + expect(wrapper.find('.makeStyles-avatarContainer-6').exists()).toBe(false); }); - it('should not add hideVideoProp to InfoContainer component when a video track is published', () => { + it('should add isSwitchedOff prop to Container component when video is switched off', () => { + mockUseIsTrackSwitchedOff.mockImplementation(() => true); mockUsePublications.mockImplementation(() => [{ trackName: 'camera-123456' }]); const wrapper = shallow( {}} isSelected={false} participant={{ identity: 'mockIdentity' } as any}> mock children ); - expect(wrapper.find('.makeStyles-infoContainer-3').prop('className')).not.toContain('hideVideo'); + expect(wrapper.find('.makeStyles-container-1').prop('className')).toContain('isVideoSwitchedOff'); }); - it('should render a VideoCamOff icon when no video tracks are published', () => { - mockUsePublications.mockImplementation(() => []); + it('should not add isSwitchedOff prop to Container component when video is not switched off', () => { + mockUseIsTrackSwitchedOff.mockImplementation(() => false); + mockUsePublications.mockImplementation(() => [{ trackName: 'camera-123456' }]); const wrapper = shallow( {}} isSelected={false} participant={{ identity: 'mockIdentity' } as any}> mock children ); - expect(wrapper.find('VideocamOffIcon').exists()).toEqual(true); + expect(wrapper.find('.makeStyles-container-1').prop('className')).not.toContain('isVideoSwitchedOff'); }); - it('should not render a VideoCamOff icon when a video track is published', () => { + it('should render the PinIcon component when the participant is selected', () => { mockUsePublications.mockImplementation(() => [{ trackName: 'camera-123456' }]); const wrapper = shallow( - {}} isSelected={false} participant={{ identity: 'mockIdentity' } as any}> + {}} isSelected={true} participant={{ identity: 'mockIdentity' } as any}> mock children ); - expect(wrapper.find('VideocamOffIcon').exists()).toEqual(false); + expect(wrapper.exists(PinIcon)).toBe(true); }); - it('should add isSwitchedOff prop to Container component when video is switched off', () => { - mockUseIsTrackSwitchedOff.mockImplementation(() => true); + it('should not render the PinIcon component when the participant is not selected', () => { mockUsePublications.mockImplementation(() => [{ trackName: 'camera-123456' }]); const wrapper = shallow( {}} isSelected={false} participant={{ identity: 'mockIdentity' } as any}> mock children ); - expect(wrapper.find('.makeStyles-container-1').prop('className')).toContain('isVideoSwitchedOff'); + expect(wrapper.exists(PinIcon)).toBe(false); }); - it('should not add isSwitchedOff prop to Container component when video is not switched off', () => { + it('should add "(You)" to the participants identity when they are the localParticipant', () => { mockUseIsTrackSwitchedOff.mockImplementation(() => false); mockUsePublications.mockImplementation(() => [{ trackName: 'camera-123456' }]); const wrapper = shallow( - {}} isSelected={false} participant={{ identity: 'mockIdentity' } as any}> + {}} + isSelected={false} + participant={{ identity: 'mockIdentity' } as any} + isLocalParticipant + > mock children ); - expect(wrapper.find('.makeStyles-container-1').prop('className')).not.toContain('isVideoSwitchedOff'); + expect(wrapper.text()).toContain('mockIdentity (You)'); }); - it('should render the PinIcon component when the participant is selected', () => { + it('should not add "(You)" to the participants identity when they are the localParticipant', () => { + mockUseIsTrackSwitchedOff.mockImplementation(() => false); mockUsePublications.mockImplementation(() => [{ trackName: 'camera-123456' }]); const wrapper = shallow( - {}} isSelected={true} participant={{ identity: 'mockIdentity' } as any}> + {}} isSelected={false} participant={{ identity: 'mockIdentity' } as any}> mock children ); - expect(wrapper.exists(PinIcon)).toBe(true); + expect(wrapper.text()).not.toContain('mockIdentity (You)'); }); - it('should not render the PinIcon component when the participant is not selected', () => { + it('should add the isDominantSpeaker class when the participant is not the dominant speaker', () => { + mockUseIsTrackSwitchedOff.mockImplementation(() => false); mockUsePublications.mockImplementation(() => [{ trackName: 'camera-123456' }]); const wrapper = shallow( - {}} isSelected={false} participant={{ identity: 'mockIdentity' } as any}> + {}} + isSelected={false} + participant={{ identity: 'mockIdentity' } as any} + isDominantSpeaker + > mock children ); - expect(wrapper.exists(PinIcon)).toBe(false); + expect(wrapper.find('.makeStyles-container-1').prop('className')).toContain('isDominantSpeaker'); }); }); From e340c0a74c96373309e8ba232dc37afe1c52d51a Mon Sep 17 00:00:00 2001 From: Tim Mendoza Date: Thu, 3 Sep 2020 21:11:04 -0600 Subject: [PATCH 06/23] Update MainParticipantInfo tests --- .../MainParticipantInfo.test.tsx | 33 ++++++++++++++++--- .../ParticipantInfo/ParticipantInfo.test.tsx | 5 +-- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/components/MainParticipantInfo/MainParticipantInfo.test.tsx b/src/components/MainParticipantInfo/MainParticipantInfo.test.tsx index 75e0ec42e..262c9a974 100644 --- a/src/components/MainParticipantInfo/MainParticipantInfo.test.tsx +++ b/src/components/MainParticipantInfo/MainParticipantInfo.test.tsx @@ -1,35 +1,44 @@ import React from 'react'; +import { Avatar } from '../../icons/Avatar'; import MainParticipantInfo from './MainParticipantInfo'; import { shallow } from 'enzyme'; import useIsTrackSwitchedOff from '../../hooks/useIsTrackSwitchedOff/useIsTrackSwitchedOff'; import usePublications from '../../hooks/usePublications/usePublications'; import useTrack from '../../hooks/useTrack/useTrack'; +import useVideoContext from '../../hooks/useVideoContext/useVideoContext'; jest.mock('../../hooks/useParticipantNetworkQualityLevel/useParticipantNetworkQualityLevel', () => () => 4); jest.mock('../../hooks/usePublications/usePublications'); jest.mock('../../hooks/useIsTrackSwitchedOff/useIsTrackSwitchedOff'); jest.mock('../../hooks/useTrack/useTrack'); +jest.mock('../../hooks/useVideoContext/useVideoContext'); const mockUsePublications = usePublications as jest.Mock; const mockUseIsTrackSwitchedOff = useIsTrackSwitchedOff as jest.Mock; const mockUseTrack = useTrack as jest.Mock; +const mockUseVideoContext = useVideoContext as jest.Mock; describe('the MainParticipantInfo component', () => { beforeEach(jest.clearAllMocks); - it('should render a VideoCamOff icon when no camera tracks are published', () => { + + beforeEach(() => { + mockUseVideoContext.mockImplementation(() => ({ room: { localParticipant: {} } })); + }); + + it('should the Avatar component when no video tracks are published', () => { mockUsePublications.mockImplementation(() => []); const wrapper = shallow( mock children ); - expect(wrapper.find('VideocamOffIcon').exists()).toEqual(true); + expect(wrapper.find(Avatar).exists()).toBe(true); }); - it('should not render a VideoCamOff icon when a camera track is published', () => { + it('should not the Avatar component when no video tracks are published', () => { mockUsePublications.mockImplementation(() => [{ trackName: 'camera-123456' }]); const wrapper = shallow( mock children ); - expect(wrapper.find('VideocamOffIcon').exists()).toEqual(false); + expect(wrapper.find(Avatar).exists()).toBe(false); }); it('should add isVideoSwitchedOff class to container div when video is switched off', () => { @@ -60,4 +69,20 @@ describe('the MainParticipantInfo component', () => { shallow(mock children); expect(mockUseTrack).toHaveBeenCalledWith({ trackName: 'camera-123456' }); }); + + it('should add "(You)" to the participants identity when they are the localParticipant', () => { + const mockParticipant = { identity: 'mockIdentity' } as any; + mockUseVideoContext.mockImplementationOnce(() => ({ room: { localParticipant: mockParticipant } })); + mockUseIsTrackSwitchedOff.mockImplementation(() => false); + const wrapper = shallow(mock children); + expect(wrapper.text()).toContain('mockIdentity (You)'); + }); + + it('should not add "(You)" to the participants identity when they are the localParticipant', () => { + mockUseIsTrackSwitchedOff.mockImplementation(() => false); + const wrapper = shallow( + mock children + ); + expect(wrapper.text()).not.toContain('mockIdentity (You)'); + }); }); diff --git a/src/components/ParticipantInfo/ParticipantInfo.test.tsx b/src/components/ParticipantInfo/ParticipantInfo.test.tsx index 06ade891d..65a0b27cf 100644 --- a/src/components/ParticipantInfo/ParticipantInfo.test.tsx +++ b/src/components/ParticipantInfo/ParticipantInfo.test.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { Avatar } from '../../icons/Avatar'; import ParticipantInfo from './ParticipantInfo'; import PinIcon from './PinIcon/PinIcon'; import { shallow } from 'enzyme'; @@ -20,7 +21,7 @@ describe('the ParticipantInfo component', () => { mock children ); - expect(wrapper.find('.makeStyles-avatarContainer-6').exists()).toBe(true); + expect(wrapper.find(Avatar).exists()).toBe(true); }); it('should not display the Avatar component when a video track is published', () => { @@ -30,7 +31,7 @@ describe('the ParticipantInfo component', () => { mock children ); - expect(wrapper.find('.makeStyles-avatarContainer-6').exists()).toBe(false); + expect(wrapper.find(Avatar).exists()).toBe(false); }); it('should add isSwitchedOff prop to Container component when video is switched off', () => { From 752044a73c19808a9b72b714b99853974971e251 Mon Sep 17 00:00:00 2001 From: Tim Mendoza Date: Thu, 3 Sep 2020 21:21:00 -0600 Subject: [PATCH 07/23] Update tests for ParticipantList --- .../ParticipantList/ParticipantList.test.tsx | 36 +++++++++++++++++++ .../ParticipantList/ParticipantList.tsx | 2 ++ ...tsx.snap => ParticipantList.test.tsx.snap} | 17 ++++++--- 3 files changed, 50 insertions(+), 5 deletions(-) rename src/components/ParticipantList/__snapshots__/{ParticipantStrip.test.tsx.snap => ParticipantList.test.tsx.snap} (58%) diff --git a/src/components/ParticipantList/ParticipantList.test.tsx b/src/components/ParticipantList/ParticipantList.test.tsx index 34ea8eacf..388759dda 100644 --- a/src/components/ParticipantList/ParticipantList.test.tsx +++ b/src/components/ParticipantList/ParticipantList.test.tsx @@ -61,4 +61,40 @@ describe('the ParticipantList component', () => { .prop('isSelected') ).toBe(true); }); + + it('should add the isDominantSpeaker prop to the participant that is the dominantSpeaker', () => { + const mockParticipant = { sid: 1, identity: 'mockDominantSpeaker' }; + const mockRoom: any = new EventEmitter(); + mockRoom.participants = new Map([ + [0, { sid: 0, identity: 'localParticipant' }], + [1, mockParticipant], + ]); + mockRoom.localParticipant = 'localParticipant'; + mockRoom.dominantSpeaker = mockParticipant; + mockedVideoContext.mockImplementation(() => ({ room: mockRoom })); + const wrapper = shallow(); + + expect( + wrapper + .find('Participant') + .at(2) + .prop('isDominantSpeaker') + ).toBe(true); + + expect( + wrapper + .find('Participant') + .at(1) + .prop('isDominantSpeaker') + ).toBe(false); + }); + + it('should not render anything when there are no remote particiants', () => { + const mockRoom: any = new EventEmitter(); + mockRoom.participants = new Map([]); + mockRoom.localParticipant = 'localParticipant'; + mockedVideoContext.mockImplementation(() => ({ room: mockRoom })); + const wrapper = shallow(); + expect(wrapper.getElement()).toBe(null); + }); }); diff --git a/src/components/ParticipantList/ParticipantList.tsx b/src/components/ParticipantList/ParticipantList.tsx index ef69cb963..b4c2b16ca 100644 --- a/src/components/ParticipantList/ParticipantList.tsx +++ b/src/components/ParticipantList/ParticipantList.tsx @@ -44,6 +44,8 @@ export default function ParticipantList() { const screenShareParticipant = useScreenShareParticipant(); const isRemoteParticipantScreenSharing = screenShareParticipant && screenShareParticipant !== localParticipant; + if (participants.length === 0) return null; // Don't render this component if there are no remote participants. + return (