403 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			TypeScript
		
	
	
			
		
		
	
	
			403 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			TypeScript
		
	
	
| /*
 | |
| Copyright 2021 The Matrix.org Foundation C.I.C.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| import React from 'react';
 | |
| import { render, fireEvent, RenderResult, waitFor } from "@testing-library/react";
 | |
| import { Room, RoomMember, MatrixError, IContent } from 'matrix-js-sdk/src/matrix';
 | |
| 
 | |
| import { stubClient } from '../../../test-utils';
 | |
| import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
 | |
| import DMRoomMap from '../../../../src/utils/DMRoomMap';
 | |
| import RoomPreviewBar from '../../../../src/components/views/rooms/RoomPreviewBar';
 | |
| import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
 | |
| 
 | |
| jest.mock('../../../../src/IdentityAuthClient', () => {
 | |
|     return jest.fn().mockImplementation(() => {
 | |
|         return { getAccessToken: jest.fn().mockResolvedValue('mock-token') };
 | |
|     });
 | |
| });
 | |
| 
 | |
| jest.useRealTimers();
 | |
| 
 | |
| const createRoom = (roomId: string, userId: string): Room => {
 | |
|     const newRoom = new Room(
 | |
|         roomId,
 | |
|         MatrixClientPeg.get(),
 | |
|         userId,
 | |
|         {},
 | |
|     );
 | |
|     DMRoomMap.makeShared().start();
 | |
|     return newRoom;
 | |
| };
 | |
| 
 | |
| const makeMockRoomMember = (
 | |
|     { userId, isKicked, membership, content, memberContent }:
 | |
|     {userId?: string;
 | |
|         isKicked?: boolean;
 | |
|         membership?: 'invite' | 'ban';
 | |
|         content?: Partial<IContent>;
 | |
|         memberContent?: Partial<IContent>;
 | |
|     },
 | |
| ) => ({
 | |
|     userId,
 | |
|     rawDisplayName: `${userId} name`,
 | |
|     isKicked: jest.fn().mockReturnValue(!!isKicked),
 | |
|     getContent: jest.fn().mockReturnValue(content || {}),
 | |
|     membership,
 | |
|     events: {
 | |
|         member: {
 | |
|             getSender: jest.fn().mockReturnValue('@kicker:test.com'),
 | |
|             getContent: jest.fn().mockReturnValue({ reason: 'test reason', ...memberContent }),
 | |
|         },
 | |
|     },
 | |
| }) as unknown as RoomMember;
 | |
| 
 | |
| describe('<RoomPreviewBar />', () => {
 | |
|     const roomId = 'RoomPreviewBar-test-room';
 | |
|     const userId = '@tester:test.com';
 | |
|     const inviterUserId = '@inviter:test.com';
 | |
|     const otherUserId = '@othertester:test.com';
 | |
| 
 | |
|     const getComponent = (props = {}) => {
 | |
|         const defaultProps = {
 | |
|             room: createRoom(roomId, userId),
 | |
|         };
 | |
|         return render(<RoomPreviewBar {...defaultProps} {...props} />);
 | |
|     };
 | |
| 
 | |
|     const isSpinnerRendered = (wrapper: RenderResult) => !!wrapper.container.querySelector('.mx_Spinner');
 | |
|     const getMessage = (wrapper: RenderResult) =>
 | |
|         wrapper.container.querySelector<HTMLDivElement>('.mx_RoomPreviewBar_message');
 | |
|     const getActions = (wrapper: RenderResult) =>
 | |
|         wrapper.container.querySelector<HTMLDivElement>('.mx_RoomPreviewBar_actions');
 | |
|     const getPrimaryActionButton = (wrapper: RenderResult) =>
 | |
|         getActions(wrapper).querySelector('.mx_AccessibleButton_kind_primary');
 | |
|     const getSecondaryActionButton = (wrapper: RenderResult) =>
 | |
|         getActions(wrapper).querySelector('.mx_AccessibleButton_kind_secondary');
 | |
| 
 | |
|     beforeEach(() => {
 | |
|         stubClient();
 | |
|         MatrixClientPeg.get().getUserId = jest.fn().mockReturnValue(userId);
 | |
|     });
 | |
| 
 | |
|     afterEach(() => {
 | |
|         const container = document.body.firstChild;
 | |
|         container && document.body.removeChild(container);
 | |
|     });
 | |
| 
 | |
|     it('renders joining message', () => {
 | |
|         const component = getComponent({ joining: true });
 | |
| 
 | |
|         expect(isSpinnerRendered(component)).toBeTruthy();
 | |
|         expect(getMessage(component).textContent).toEqual('Joining …');
 | |
|     });
 | |
|     it('renders rejecting message', () => {
 | |
|         const component = getComponent({ rejecting: true });
 | |
|         expect(isSpinnerRendered(component)).toBeTruthy();
 | |
|         expect(getMessage(component).textContent).toEqual('Rejecting invite …');
 | |
|     });
 | |
|     it('renders loading message', () => {
 | |
|         const component = getComponent({ loading: true });
 | |
|         expect(isSpinnerRendered(component)).toBeTruthy();
 | |
|         expect(getMessage(component).textContent).toEqual('Loading …');
 | |
|     });
 | |
| 
 | |
|     it('renders not logged in message', () => {
 | |
|         MatrixClientPeg.get().isGuest = jest.fn().mockReturnValue(true);
 | |
|         const component = getComponent({ loading: true });
 | |
| 
 | |
|         expect(isSpinnerRendered(component)).toBeFalsy();
 | |
|         expect(getMessage(component).textContent).toEqual('Join the conversation with an account');
 | |
|     });
 | |
| 
 | |
|     it("should send room oob data to start login", async () => {
 | |
|         MatrixClientPeg.get().isGuest = jest.fn().mockReturnValue(true);
 | |
|         const component = getComponent({
 | |
|             oobData: {
 | |
|                 name: "Room Name",
 | |
|                 avatarUrl: "mxc://foo/bar",
 | |
|                 inviterName: "Charlie",
 | |
|             },
 | |
|         });
 | |
| 
 | |
|         const dispatcherSpy = jest.fn();
 | |
|         const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
 | |
| 
 | |
|         expect(getMessage(component).textContent).toEqual('Join the conversation with an account');
 | |
|         fireEvent.click(getPrimaryActionButton(component));
 | |
| 
 | |
|         await waitFor(() => expect(dispatcherSpy).toHaveBeenCalledWith(expect.objectContaining({
 | |
|             screenAfterLogin: {
 | |
|                 screen: 'room',
 | |
|                 params: expect.objectContaining({
 | |
|                     room_name: "Room Name",
 | |
|                     room_avatar_url: "mxc://foo/bar",
 | |
|                     inviter_name: "Charlie",
 | |
|                 }),
 | |
|             },
 | |
|         })));
 | |
| 
 | |
|         defaultDispatcher.unregister(dispatcherRef);
 | |
|     });
 | |
| 
 | |
|     it('renders kicked message', () => {
 | |
|         const room = createRoom(roomId, otherUserId);
 | |
|         jest.spyOn(room, 'getMember').mockReturnValue(makeMockRoomMember({ isKicked: true }));
 | |
|         const component = getComponent({ loading: true, room });
 | |
| 
 | |
|         expect(getMessage(component)).toMatchSnapshot();
 | |
|     });
 | |
| 
 | |
|     it('renders banned message', () => {
 | |
|         const room = createRoom(roomId, otherUserId);
 | |
|         jest.spyOn(room, 'getMember').mockReturnValue(makeMockRoomMember({ membership: 'ban' }));
 | |
|         const component = getComponent({ loading: true, room });
 | |
| 
 | |
|         expect(getMessage(component)).toMatchSnapshot();
 | |
|     });
 | |
| 
 | |
|     describe('with an error', () => {
 | |
|         it('renders room not found error', () => {
 | |
|             const error = new MatrixError({
 | |
|                 errcode: 'M_NOT_FOUND',
 | |
|                 error: "Room not found",
 | |
|             });
 | |
|             const component = getComponent({ error });
 | |
| 
 | |
|             expect(getMessage(component)).toMatchSnapshot();
 | |
|         });
 | |
|         it('renders other errors', () => {
 | |
|             const error = new MatrixError({
 | |
|                 errcode: 'Something_else',
 | |
|             });
 | |
|             const component = getComponent({ error });
 | |
| 
 | |
|             expect(getMessage(component)).toMatchSnapshot();
 | |
|         });
 | |
|     });
 | |
| 
 | |
|     it('renders viewing room message when room an be previewed', () => {
 | |
|         const component = getComponent({ canPreview: true });
 | |
| 
 | |
|         expect(getMessage(component)).toMatchSnapshot();
 | |
|     });
 | |
| 
 | |
|     it('renders viewing room message when room can not be previewed', () => {
 | |
|         const component = getComponent({ canPreview: false });
 | |
| 
 | |
|         expect(getMessage(component)).toMatchSnapshot();
 | |
|     });
 | |
| 
 | |
|     describe('with an invite', () => {
 | |
|         const inviterName = inviterUserId;
 | |
|         const userMember = makeMockRoomMember({ userId });
 | |
|         const userMemberWithDmInvite = makeMockRoomMember({
 | |
|             userId, membership: 'invite', memberContent: { is_direct: true },
 | |
|         });
 | |
|         const inviterMember = makeMockRoomMember({
 | |
|             userId: inviterUserId,
 | |
|             content: {
 | |
|                 "reason": 'test',
 | |
|                 'io.element.html_reason': '<h3>hello</h3>',
 | |
|             },
 | |
|         });
 | |
|         describe('without an invited email', () => {
 | |
|             describe('for a non-dm room', () => {
 | |
|                 const mockGetMember = (id) => {
 | |
|                     if (id === userId) return userMember;
 | |
|                     return inviterMember;
 | |
|                 };
 | |
|                 const onJoinClick = jest.fn();
 | |
|                 const onRejectClick = jest.fn();
 | |
|                 let room;
 | |
| 
 | |
|                 beforeEach(() => {
 | |
|                     room = createRoom(roomId, userId);
 | |
|                     jest.spyOn(room, 'getMember').mockImplementation(mockGetMember);
 | |
|                     jest.spyOn(room.currentState, 'getMember').mockImplementation(mockGetMember);
 | |
|                     onJoinClick.mockClear();
 | |
|                     onRejectClick.mockClear();
 | |
|                 });
 | |
| 
 | |
|                 it('renders invite message', () => {
 | |
|                     const component = getComponent({ inviterName, room });
 | |
|                     expect(getMessage(component)).toMatchSnapshot();
 | |
|                 });
 | |
| 
 | |
|                 it('renders join and reject action buttons correctly', () => {
 | |
|                     const component = getComponent({ inviterName, room, onJoinClick, onRejectClick });
 | |
|                     expect(getActions(component)).toMatchSnapshot();
 | |
|                 });
 | |
| 
 | |
|                 it('renders reject and ignore action buttons when handler is provided', () => {
 | |
|                     const onRejectAndIgnoreClick = jest.fn();
 | |
|                     const component = getComponent({
 | |
|                         inviterName, room, onJoinClick, onRejectClick, onRejectAndIgnoreClick,
 | |
|                     });
 | |
|                     expect(getActions(component)).toMatchSnapshot();
 | |
|                 });
 | |
| 
 | |
|                 it('renders join and reject action buttons in reverse order when room can previewed', () => {
 | |
|                     // when room is previewed action buttons are rendered left to right, with primary on the right
 | |
|                     const component = getComponent({ inviterName, room, onJoinClick, onRejectClick, canPreview: true });
 | |
|                     expect(getActions(component)).toMatchSnapshot();
 | |
|                 });
 | |
| 
 | |
|                 it('joins room on primary button click', () => {
 | |
|                     const component = getComponent({ inviterName, room, onJoinClick, onRejectClick });
 | |
|                     fireEvent.click(getPrimaryActionButton(component));
 | |
| 
 | |
|                     expect(onJoinClick).toHaveBeenCalled();
 | |
|                 });
 | |
| 
 | |
|                 it('rejects invite on secondary button click', () => {
 | |
|                     const component = getComponent({ inviterName, room, onJoinClick, onRejectClick });
 | |
|                     fireEvent.click(getSecondaryActionButton(component));
 | |
| 
 | |
|                     expect(onRejectClick).toHaveBeenCalled();
 | |
|                 });
 | |
|             });
 | |
| 
 | |
|             describe('for a dm room', () => {
 | |
|                 const mockGetMember = (id) => {
 | |
|                     if (id === userId) return userMemberWithDmInvite;
 | |
|                     return inviterMember;
 | |
|                 };
 | |
|                 const onJoinClick = jest.fn();
 | |
|                 const onRejectClick = jest.fn();
 | |
|                 let room;
 | |
| 
 | |
|                 beforeEach(() => {
 | |
|                     room = createRoom(roomId, userId);
 | |
|                     jest.spyOn(room, 'getMember').mockImplementation(mockGetMember);
 | |
|                     jest.spyOn(room.currentState, 'getMember').mockImplementation(mockGetMember);
 | |
|                     onJoinClick.mockClear();
 | |
|                     onRejectClick.mockClear();
 | |
|                 });
 | |
| 
 | |
|                 it('renders invite message to a non-dm room', () => {
 | |
|                     const component = getComponent({ inviterName, room });
 | |
|                     expect(getMessage(component)).toMatchSnapshot();
 | |
|                 });
 | |
| 
 | |
|                 it('renders join and reject action buttons with correct labels', () => {
 | |
|                     const onRejectAndIgnoreClick = jest.fn();
 | |
|                     const component = getComponent({
 | |
|                         inviterName, room, onJoinClick, onRejectAndIgnoreClick, onRejectClick,
 | |
|                     });
 | |
|                     expect(getActions(component)).toMatchSnapshot();
 | |
|                 });
 | |
|             });
 | |
|         });
 | |
| 
 | |
|         describe('with an invited email', () => {
 | |
|             const invitedEmail = 'test@test.com';
 | |
|             const mockThreePids = [
 | |
|                 { medium: 'email', address: invitedEmail },
 | |
|                 { medium: 'not-email', address: 'address 2' },
 | |
|             ];
 | |
| 
 | |
|             const testJoinButton = (props) => async () => {
 | |
|                 const onJoinClick = jest.fn();
 | |
|                 const onRejectClick = jest.fn();
 | |
|                 const component = getComponent({ ...props, onJoinClick, onRejectClick });
 | |
|                 await new Promise(setImmediate);
 | |
|                 expect(getPrimaryActionButton(component)).toBeTruthy();
 | |
|                 expect(getSecondaryActionButton(component)).toBeFalsy();
 | |
|                 fireEvent.click(getPrimaryActionButton(component));
 | |
|                 expect(onJoinClick).toHaveBeenCalled();
 | |
|             };
 | |
| 
 | |
|             describe('when client fails to get 3PIDs', () => {
 | |
|                 beforeEach(() => {
 | |
|                     MatrixClientPeg.get().getThreePids = jest.fn().mockRejectedValue({ errCode: 'TEST_ERROR' });
 | |
|                 });
 | |
| 
 | |
|                 it('renders error message', async () => {
 | |
|                     const component = getComponent({ inviterName, invitedEmail });
 | |
|                     await new Promise(setImmediate);
 | |
| 
 | |
|                     expect(getMessage(component)).toMatchSnapshot();
 | |
|                 });
 | |
| 
 | |
|                 it('renders join button', testJoinButton({ inviterName, invitedEmail }));
 | |
|             });
 | |
| 
 | |
|             describe('when invitedEmail is not associated with current account', () => {
 | |
|                 beforeEach(() => {
 | |
|                     MatrixClientPeg.get().getThreePids = jest.fn().mockResolvedValue(
 | |
|                         { threepids: mockThreePids.slice(1) },
 | |
|                     );
 | |
|                 });
 | |
| 
 | |
|                 it('renders invite message with invited email', async () => {
 | |
|                     const component = getComponent({ inviterName, invitedEmail });
 | |
|                     await new Promise(setImmediate);
 | |
| 
 | |
|                     expect(getMessage(component)).toMatchSnapshot();
 | |
|                 });
 | |
| 
 | |
|                 it('renders join button', testJoinButton({ inviterName, invitedEmail }));
 | |
|             });
 | |
| 
 | |
|             describe('when client has no identity server connected', () => {
 | |
|                 beforeEach(() => {
 | |
|                     MatrixClientPeg.get().getThreePids = jest.fn().mockResolvedValue({ threepids: mockThreePids });
 | |
|                     MatrixClientPeg.get().getIdentityServerUrl = jest.fn().mockReturnValue(false);
 | |
|                 });
 | |
| 
 | |
|                 it('renders invite message with invited email', async () => {
 | |
|                     const component = getComponent({ inviterName, invitedEmail });
 | |
|                     await new Promise(setImmediate);
 | |
| 
 | |
|                     expect(getMessage(component)).toMatchSnapshot();
 | |
|                 });
 | |
| 
 | |
|                 it('renders join button', testJoinButton({ inviterName, invitedEmail }));
 | |
|             });
 | |
| 
 | |
|             describe('when client has an identity server connected', () => {
 | |
|                 beforeEach(() => {
 | |
|                     MatrixClientPeg.get().getThreePids = jest.fn().mockResolvedValue({ threepids: mockThreePids });
 | |
|                     MatrixClientPeg.get().getIdentityServerUrl = jest.fn().mockReturnValue('identity.test');
 | |
|                     MatrixClientPeg.get().lookupThreePid = jest.fn().mockResolvedValue('identity.test');
 | |
|                 });
 | |
| 
 | |
|                 it('renders email mismatch message when invite email mxid doesnt match', async () => {
 | |
|                     MatrixClientPeg.get().lookupThreePid = jest.fn().mockReturnValue('not userid');
 | |
|                     const component = getComponent({ inviterName, invitedEmail });
 | |
|                     await new Promise(setImmediate);
 | |
| 
 | |
|                     expect(getMessage(component)).toMatchSnapshot();
 | |
|                     expect(MatrixClientPeg.get().lookupThreePid).toHaveBeenCalledWith(
 | |
|                         'email', invitedEmail, 'mock-token',
 | |
|                     );
 | |
|                     await testJoinButton({ inviterName, invitedEmail })();
 | |
|                 });
 | |
| 
 | |
|                 it('renders invite message when invite email mxid match', async () => {
 | |
|                     MatrixClientPeg.get().lookupThreePid = jest.fn().mockReturnValue(userId);
 | |
|                     const component = getComponent({ inviterName, invitedEmail });
 | |
|                     await new Promise(setImmediate);
 | |
| 
 | |
|                     expect(getMessage(component)).toMatchSnapshot();
 | |
|                     await testJoinButton({ inviterName, invitedEmail })();
 | |
|                 });
 | |
|             });
 | |
|         });
 | |
|     });
 | |
| });
 |