384 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			TypeScript
		
	
	
			
		
		
	
	
			384 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 {
 | 
						|
    renderIntoDocument,
 | 
						|
    Simulate,
 | 
						|
    findRenderedDOMComponentWithClass,
 | 
						|
    act,
 | 
						|
} from 'react-dom/test-utils';
 | 
						|
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';
 | 
						|
 | 
						|
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),
 | 
						|
        };
 | 
						|
        const wrapper = renderIntoDocument<React.Component>(
 | 
						|
            <RoomPreviewBar {...defaultProps} {...props} />,
 | 
						|
        ) as React.Component;
 | 
						|
        return findRenderedDOMComponentWithClass(wrapper, 'mx_RoomPreviewBar') as HTMLDivElement;
 | 
						|
    };
 | 
						|
 | 
						|
    const isSpinnerRendered = (element: Element) => !!element.querySelector('.mx_Spinner');
 | 
						|
    const getMessage = (element: Element) => element.querySelector<HTMLDivElement>('.mx_RoomPreviewBar_message');
 | 
						|
    const getActions = (element: Element) => element.querySelector<HTMLDivElement>('.mx_RoomPreviewBar_actions');
 | 
						|
    const getPrimaryActionButton = (element: Element) =>
 | 
						|
        getActions(element).querySelector('.mx_AccessibleButton_kind_primary');
 | 
						|
    const getSecondaryActionButton = (element: Element) =>
 | 
						|
        getActions(element).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('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 });
 | 
						|
                    act(() => {
 | 
						|
                        Simulate.click(getPrimaryActionButton(component));
 | 
						|
                    });
 | 
						|
 | 
						|
                    expect(onJoinClick).toHaveBeenCalled();
 | 
						|
                });
 | 
						|
 | 
						|
                it('rejects invite on secondary button click', () => {
 | 
						|
                    const component = getComponent({ inviterName, room, onJoinClick, onRejectClick });
 | 
						|
                    act(() => {
 | 
						|
                        Simulate.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();
 | 
						|
                act(() => {
 | 
						|
                    Simulate.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, undefined, '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 })();
 | 
						|
                });
 | 
						|
            });
 | 
						|
        });
 | 
						|
    });
 | 
						|
});
 |