/* * Wire * Copyright (C) 2022 Wire Swiss GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * */ import {fireEvent, render, waitFor} from '@testing-library/react'; import {QualifiedId} from '@wireapp/api-client/lib/user'; import {amplify} from 'amplify'; import ko from 'knockout'; import {Runtime} from '@wireapp/commons'; import * as uiKit from '@wireapp/react-ui-kit'; import {WebAppEvents} from '@wireapp/webapp-events'; import {TitleBar} from 'Components/TitleBar'; import {withTheme} from 'src/script/auth/util/test/TestUtil'; import {ContentState} from 'src/script/page/useAppState'; import {TestFactory} from '../../../../test/helper/TestFactory'; import {CallingRepository} from '../../calling/CallingRepository'; import {CallState} from '../../calling/CallState'; import {ConversationVerificationState} from '../../conversation/ConversationVerificationState'; import {Conversation} from '../../entity/Conversation'; import {User} from '../../entity/User'; import {PanelState} from '../../page/RightSidebar/RightSidebar'; import {TeamState} from '../../team/TeamState'; import {ViewModelRepositories} from '../../view_model/MainViewModel'; jest.mock('@wireapp/react-ui-kit', () => ({ ...(jest.requireActual('@wireapp/react-ui-kit') as any), useMatchMedia: jest.fn(), })); const mockedUiKit = uiKit as jest.Mocked; jest.spyOn(Runtime, 'isSupportingConferenceCalling').mockReturnValue(true); const testFactory = new TestFactory(); let callingRepository: CallingRepository; beforeAll(() => { return testFactory.exposeCallingActors().then(injectedCallingRepository => { callingRepository = injectedCallingRepository; return callingRepository; }); }); const callActions = { answer: jest.fn(), changePage: jest.fn(), leave: jest.fn(), reject: jest.fn(), startAudio: jest.fn(), startVideo: jest.fn(), switchCameraInput: jest.fn(), switchScreenInput: jest.fn(), toggleCamera: jest.fn(), toggleMute: jest.fn(), toggleScreenshare: jest.fn(), }; const getDefaultProps = (callingRepository: CallingRepository, conversation: Conversation) => ({ callActions, callState: new CallState(), conversation, openRightSidebar: jest.fn(), repositories: { calling: { supportsConferenceCalling: true, } as CallingRepository, } as ViewModelRepositories, teamState: new TeamState(), selfUser: new User(), }); describe('TitleBar', () => { it('subscribes to shortcut PEOPLE and add ADD_PEOPLE events on mount', async () => { spyOn(amplify, 'subscribe').and.returnValue(undefined); const conversation = new Conversation(); await render(withTheme()); await waitFor(() => { expect(amplify.subscribe).toHaveBeenCalledWith(WebAppEvents.SHORTCUT.PEOPLE, expect.anything()); expect(amplify.subscribe).toHaveBeenCalledWith(WebAppEvents.SHORTCUT.ADD_PEOPLE, expect.anything()); }); }); it("doesn't show conversation search button for user with activated account", async () => { const selfUser = createUser(false); const conversation = new Conversation(); const {queryByText} = render( withTheme(), ); expect(queryByText('tooltipConversationSearch')).toBeNull(); }); it('opens search area after search button click', async () => { const selfUser = createUser(true); const conversation = new Conversation(); const {getByText} = render( withTheme(), ); const searchButton = getByText('tooltipConversationSearch'); expect(searchButton).toBeDefined(); spyOn(amplify, 'publish').and.returnValue(undefined); fireEvent.click(searchButton); expect(amplify.publish).toHaveBeenCalledWith(WebAppEvents.CONTENT.SWITCH, ContentState.COLLECTION); }); it('opens conversation details on conversation name click', async () => { const selfUser = createUser(true); const displayName = 'test name'; const conversation = createConversationEntity({ display_name: ko.pureComputed(() => displayName), }); const props = getDefaultProps(callingRepository, conversation); const {getByText} = render(withTheme()); const conversationName = getByText(displayName); expect(conversationName).toBeDefined(); fireEvent.click(conversationName); expect(props.openRightSidebar).toHaveBeenCalledWith(PanelState.CONVERSATION_DETAILS, {entity: conversation}); }); it('opens conversation details on info button click', async () => { const selfUser = createUser(true); const conversation = createConversationEntity(); const props = getDefaultProps(callingRepository, conversation); mockedUiKit.useMatchMedia.mockReturnValue(false); const {getByLabelText} = render(withTheme()); const infoButton = getByLabelText('tooltipConversationInfo'); expect(infoButton).toBeDefined(); fireEvent.click(infoButton); expect(props.openRightSidebar).toHaveBeenCalledWith(PanelState.CONVERSATION_DETAILS, {entity: conversation}); }); it('hide info button and search button on scaled down view', async () => { mockedUiKit.useMatchMedia.mockReturnValue(true); const conversation = new Conversation(); const {queryByLabelText} = render(withTheme()); const infoButton = queryByLabelText('tooltipConversationInfo'); const videoCallButton = queryByLabelText('tooltipConversationVideoCall'); expect(infoButton).toBe(null); expect(videoCallButton).toBe(null); }); it("doesn't show legal-hold icon for non legal-hold user", async () => { const selfUser = createUser(true); const conversation = createConversationEntity({hasLegalHold: ko.pureComputed(() => false)}); const {container} = render( withTheme(), ); const legalHoldDotButton = container.querySelector('button[data-uie-name="status-legal-hold-conversation"]'); expect(legalHoldDotButton).toBeNull(); }); it('shows legal-hold icon for legal-hold user', async () => { const selfUser = createUser(true); const conversation = createConversationEntity({hasLegalHold: ko.pureComputed(() => true)}); const {container} = render( withTheme(), ); const legalHoldDotButton = container.querySelector('button[data-uie-name="status-legal-hold-conversation"]'); expect(legalHoldDotButton).not.toBeNull(); }); it.each([ConversationVerificationState.UNVERIFIED, ConversationVerificationState.DEGRADED])( "doesn't show verified icon in not-verified conversation", async state => { const selfUser = createUser(true); const conversation = createConversationEntity({ verification_state: ko.observable(state), }); const {queryByTestId} = render( withTheme(), ); const verifiedIcon = queryByTestId('proteus-conversations-verified'); expect(verifiedIcon).toBeNull(); }, ); it('shows verified icon in verified conversation', async () => { const selfUser = createUser(true); const conversation = createConversationEntity({ verification_state: ko.observable(ConversationVerificationState.VERIFIED), }); const {getByTestId} = render( withTheme(), ); const verifiedIcon = getByTestId('proteus-conversation-verified'); expect(verifiedIcon).not.toBeNull(); }); it('starts audio call on audio call button click', async () => { const firstUser = new User(); const conversation = createConversationEntity({ firstUserEntity: ko.pureComputed(() => firstUser), is1to1: ko.pureComputed(() => true), participating_user_ids: ko.observableArray([ {domain: '', id: ''}, {domain: '', id: ''}, ] as QualifiedId[]), }); const {getByLabelText} = render(withTheme()); const audioCallButton = getByLabelText('tooltipConversationCall'); expect(audioCallButton).toBeDefined(); fireEvent.click(audioCallButton); expect(callActions.startAudio).toHaveBeenCalledWith(conversation); }); it("doesn't show video call button when video calling is not enabled", async () => { const firstUser = new User(); const conversation = createConversationEntity({ firstUserEntity: ko.pureComputed(() => firstUser), is1to1: ko.pureComputed(() => true), participating_user_ids: ko.observableArray([ {domain: '', id: ''}, {domain: '', id: ''}, ] as QualifiedId[]), }); const teamState = createTeamState({isVideoCallingEnabled: ko.pureComputed(() => false)}); const {queryByLabelText} = render( withTheme(), ); const videoCallButton = queryByLabelText('tooltipConversationVideoCall'); expect(videoCallButton).toBeNull(); }); it('starts video call on video call button click', async () => { mockedUiKit.useMatchMedia.mockReturnValue(false); const firstUser = new User(); const teamState = createTeamState({isVideoCallingEnabled: ko.pureComputed(() => true)}); const conversation = createConversationEntity({ firstUserEntity: ko.pureComputed(() => firstUser), is1to1: ko.pureComputed(() => true), participating_user_ids: ko.observableArray([ {domain: '', id: ''}, {domain: '', id: ''}, ] as QualifiedId[]), }); const {getByLabelText} = render( withTheme(), ); const videoCallButton = getByLabelText('tooltipConversationVideoCall'); expect(videoCallButton).toBeDefined(); fireEvent.click(videoCallButton); expect(callActions.startVideo).toHaveBeenCalledWith(conversation); }); it('displays warning badge', async () => { const conversation = createConversationEntity({ hasDirectGuest: ko.pureComputed(() => true), isGroup: ko.pureComputed(() => true), }); const {getByText} = render(withTheme()); expect(getByText('guestRoomConversationBadge')).toBeDefined(); }); }); function createUser(activated: boolean = true) { const user = new User(); user.isTemporaryGuest(!activated); return user; } function createTeamState(team?: Partial) { const teamState = new TeamState(); return Object.assign(teamState, team) as TeamState; } function createConversationEntity(conversation?: Partial) { const conversationEntity = new Conversation(); return Object.assign(conversationEntity, conversation) as Conversation; }