279 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
			
		
		
	
	
			279 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
/*
 | 
						|
Copyright 2024 New Vector Ltd.
 | 
						|
Copyright 2023 The Matrix.org Foundation C.I.C.
 | 
						|
 | 
						|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
 | 
						|
Please see LICENSE files in the repository root for full details.
 | 
						|
*/
 | 
						|
 | 
						|
import React from "react";
 | 
						|
import { render, RenderResult, screen } from "jest-matrix-react";
 | 
						|
import userEvent from "@testing-library/user-event";
 | 
						|
import { mocked, Mocked } from "jest-mock";
 | 
						|
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
 | 
						|
 | 
						|
import dis from "../../../../../src/dispatcher/dispatcher";
 | 
						|
import { Pill, PillProps, PillType } from "../../../../../src/components/views/elements/Pill";
 | 
						|
import {
 | 
						|
    filterConsole,
 | 
						|
    flushPromises,
 | 
						|
    mkMessage,
 | 
						|
    mkRoomCanonicalAliasEvent,
 | 
						|
    mkRoomMemberJoinEvent,
 | 
						|
    stubClient,
 | 
						|
} from "../../../../test-utils";
 | 
						|
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
 | 
						|
import { Action } from "../../../../../src/dispatcher/actions";
 | 
						|
import { ButtonEvent } from "../../../../../src/components/views/elements/AccessibleButton";
 | 
						|
import { SdkContextClass } from "../../../../../src/contexts/SDKContext";
 | 
						|
 | 
						|
describe("<Pill>", () => {
 | 
						|
    let client: Mocked<MatrixClient>;
 | 
						|
    const permalinkPrefix = "https://matrix.to/#/";
 | 
						|
    const room1Alias = "#room1:example.com";
 | 
						|
    const room1Id = "!room1:example.com";
 | 
						|
    let room1: Room;
 | 
						|
    let room1Message: MatrixEvent;
 | 
						|
    const room2Id = "!room2:example.com";
 | 
						|
    let room2: Room;
 | 
						|
    const space1Id = "!space1:example.com";
 | 
						|
    let space1: Room;
 | 
						|
    const user1Id = "@user1:example.com";
 | 
						|
    const user2Id = "@user2:example.com";
 | 
						|
    const user3Id = "@user3:example.com";
 | 
						|
    let renderResult: RenderResult;
 | 
						|
    let pillParentClickHandler: (e: ButtonEvent) => void;
 | 
						|
 | 
						|
    const renderPill = (props: PillProps): void => {
 | 
						|
        const withDefault = {
 | 
						|
            inMessage: true,
 | 
						|
            shouldShowPillAvatar: true,
 | 
						|
            ...props,
 | 
						|
        } as PillProps;
 | 
						|
        // wrap Pill with a div to allow testing of event bubbling
 | 
						|
        renderResult = render(
 | 
						|
            // eslint-disable-next-line jsx-a11y/click-events-have-key-events
 | 
						|
            <div onClick={pillParentClickHandler}>
 | 
						|
                <Pill {...withDefault} />
 | 
						|
            </div>,
 | 
						|
        );
 | 
						|
    };
 | 
						|
 | 
						|
    filterConsole(
 | 
						|
        "Failed to parse permalink Error: Unknown entity type in permalink",
 | 
						|
        "Room !room1:example.com does not have an m.room.create event",
 | 
						|
        "Room !space1:example.com does not have an m.room.create event",
 | 
						|
    );
 | 
						|
 | 
						|
    beforeEach(() => {
 | 
						|
        client = mocked(stubClient());
 | 
						|
        SdkContextClass.instance.client = client;
 | 
						|
        DMRoomMap.makeShared(client);
 | 
						|
        room1 = new Room(room1Id, client, user1Id);
 | 
						|
        room1.name = "Room 1";
 | 
						|
        const user1JoinRoom1Event = mkRoomMemberJoinEvent(user1Id, room1Id, {
 | 
						|
            displayname: "User 1",
 | 
						|
        });
 | 
						|
        room1.currentState.setStateEvents([
 | 
						|
            mkRoomCanonicalAliasEvent(user1Id, room1Id, room1Alias),
 | 
						|
            user1JoinRoom1Event,
 | 
						|
        ]);
 | 
						|
        room1.getMember(user1Id)!.setMembershipEvent(user1JoinRoom1Event);
 | 
						|
        room1Message = mkMessage({
 | 
						|
            id: "$123-456",
 | 
						|
            event: true,
 | 
						|
            user: user1Id,
 | 
						|
            room: room1Id,
 | 
						|
            msg: "Room 1 Message",
 | 
						|
        });
 | 
						|
        room1.addLiveEvents([room1Message]);
 | 
						|
 | 
						|
        room2 = new Room(room2Id, client, user1Id);
 | 
						|
        room2.currentState.setStateEvents([mkRoomMemberJoinEvent(user2Id, room2Id)]);
 | 
						|
        room2.name = "Room 2";
 | 
						|
 | 
						|
        space1 = new Room(space1Id, client, client.getSafeUserId());
 | 
						|
        space1.name = "Space 1";
 | 
						|
 | 
						|
        client.getRooms.mockReturnValue([room1, room2, space1]);
 | 
						|
        client.getRoom.mockImplementation((roomId: string) => {
 | 
						|
            if (roomId === room1.roomId) return room1;
 | 
						|
            if (roomId === room2.roomId) return room2;
 | 
						|
            if (roomId === space1.roomId) return space1;
 | 
						|
            return null;
 | 
						|
        });
 | 
						|
 | 
						|
        client.getProfileInfo.mockImplementation(async (userId: string) => {
 | 
						|
            if (userId === user2Id) return { displayname: "User 2" };
 | 
						|
            throw new Error(`Unknown user ${userId}`);
 | 
						|
        });
 | 
						|
 | 
						|
        jest.spyOn(dis, "dispatch");
 | 
						|
        pillParentClickHandler = jest.fn();
 | 
						|
 | 
						|
        jest.spyOn(global.Math, "random").mockReturnValue(0.123456);
 | 
						|
    });
 | 
						|
 | 
						|
    afterEach(() => {
 | 
						|
        jest.spyOn(global.Math, "random").mockRestore();
 | 
						|
    });
 | 
						|
 | 
						|
    describe("when rendering a pill for a room", () => {
 | 
						|
        beforeEach(() => {
 | 
						|
            renderPill({
 | 
						|
                url: permalinkPrefix + room1Id,
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        it("should render the expected pill", () => {
 | 
						|
            expect(renderResult.asFragment()).toMatchSnapshot();
 | 
						|
        });
 | 
						|
 | 
						|
        describe("when hovering the pill", () => {
 | 
						|
            beforeEach(async () => {
 | 
						|
                await userEvent.hover(screen.getByText("Room 1"));
 | 
						|
            });
 | 
						|
 | 
						|
            it("should show a tooltip with the room Id", async () => {
 | 
						|
                expect(await screen.findByRole("tooltip", { name: room1Id })).toBeInTheDocument();
 | 
						|
            });
 | 
						|
 | 
						|
            describe("when not hovering the pill any more", () => {
 | 
						|
                beforeEach(async () => {
 | 
						|
                    await userEvent.unhover(screen.getByText("Room 1"));
 | 
						|
                });
 | 
						|
 | 
						|
                it("should dimiss a tooltip with the room Id", () => {
 | 
						|
                    expect(screen.queryByRole("tooltip")).not.toBeInTheDocument();
 | 
						|
                });
 | 
						|
            });
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    it("should not render a non-permalink", () => {
 | 
						|
        renderPill({
 | 
						|
            url: "https://example.com/hello",
 | 
						|
        });
 | 
						|
        expect(renderResult.asFragment()).toMatchSnapshot();
 | 
						|
    });
 | 
						|
 | 
						|
    it("should render the expected pill for a space", () => {
 | 
						|
        renderPill({
 | 
						|
            url: permalinkPrefix + space1Id,
 | 
						|
        });
 | 
						|
        expect(renderResult.asFragment()).toMatchSnapshot();
 | 
						|
    });
 | 
						|
 | 
						|
    it("should render the expected pill for a room alias", () => {
 | 
						|
        renderPill({
 | 
						|
            url: permalinkPrefix + room1Alias,
 | 
						|
        });
 | 
						|
        expect(renderResult.asFragment()).toMatchSnapshot();
 | 
						|
    });
 | 
						|
 | 
						|
    it("should render the expected pill for @room", () => {
 | 
						|
        renderPill({
 | 
						|
            room: room1,
 | 
						|
            type: PillType.AtRoomMention,
 | 
						|
        });
 | 
						|
        expect(renderResult.asFragment()).toMatchSnapshot();
 | 
						|
    });
 | 
						|
 | 
						|
    describe("when rendering a pill for a user in the room", () => {
 | 
						|
        beforeEach(() => {
 | 
						|
            renderPill({
 | 
						|
                room: room1,
 | 
						|
                url: permalinkPrefix + user1Id,
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        it("should render as expected", () => {
 | 
						|
            expect(renderResult.asFragment()).toMatchSnapshot();
 | 
						|
        });
 | 
						|
 | 
						|
        describe("when clicking the pill", () => {
 | 
						|
            beforeEach(async () => {
 | 
						|
                await userEvent.click(screen.getByText("User 1"));
 | 
						|
            });
 | 
						|
 | 
						|
            it("should dipsatch a view user action and prevent event bubbling", () => {
 | 
						|
                expect(dis.dispatch).toHaveBeenCalledWith({
 | 
						|
                    action: Action.ViewUser,
 | 
						|
                    member: room1.getMember(user1Id),
 | 
						|
                });
 | 
						|
 | 
						|
                expect(pillParentClickHandler).not.toHaveBeenCalled();
 | 
						|
            });
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    it("should render the expected pill for a known user not in the room", async () => {
 | 
						|
        renderPill({
 | 
						|
            room: room1,
 | 
						|
            url: permalinkPrefix + user2Id,
 | 
						|
        });
 | 
						|
 | 
						|
        // wait for profile query via API
 | 
						|
        await flushPromises();
 | 
						|
 | 
						|
        expect(renderResult.asFragment()).toMatchSnapshot();
 | 
						|
    });
 | 
						|
 | 
						|
    it("should render the expected pill for an uknown user not in the room", async () => {
 | 
						|
        renderPill({
 | 
						|
            room: room1,
 | 
						|
            url: permalinkPrefix + user3Id,
 | 
						|
        });
 | 
						|
 | 
						|
        // wait for profile query via API
 | 
						|
        await flushPromises();
 | 
						|
 | 
						|
        expect(renderResult.asFragment()).toMatchSnapshot();
 | 
						|
    });
 | 
						|
 | 
						|
    it("should not render anything if the type cannot be detected", () => {
 | 
						|
        renderPill({
 | 
						|
            url: permalinkPrefix,
 | 
						|
        });
 | 
						|
        expect(renderResult.asFragment()).toMatchInlineSnapshot(`
 | 
						|
            <DocumentFragment>
 | 
						|
              <div />
 | 
						|
            </DocumentFragment>
 | 
						|
        `);
 | 
						|
    });
 | 
						|
 | 
						|
    it("should not render an avatar or link when called with inMessage = false and shouldShowPillAvatar = false", () => {
 | 
						|
        renderPill({
 | 
						|
            inMessage: false,
 | 
						|
            shouldShowPillAvatar: false,
 | 
						|
            url: permalinkPrefix + room1Id,
 | 
						|
        });
 | 
						|
        expect(renderResult.asFragment()).toMatchSnapshot();
 | 
						|
    });
 | 
						|
    it("should render the expected pill for a message in the same room", () => {
 | 
						|
        renderPill({
 | 
						|
            room: room1,
 | 
						|
            url: `${permalinkPrefix}${room1Id}/${room1Message.getId()}`,
 | 
						|
        });
 | 
						|
        expect(renderResult.asFragment()).toMatchSnapshot();
 | 
						|
    });
 | 
						|
 | 
						|
    it("should render the expected pill for a message in another room", () => {
 | 
						|
        renderPill({
 | 
						|
            room: room2,
 | 
						|
            url: `${permalinkPrefix}${room1Id}/${room1Message.getId()}`,
 | 
						|
        });
 | 
						|
        expect(renderResult.asFragment()).toMatchSnapshot();
 | 
						|
    });
 | 
						|
 | 
						|
    it("should not render a pill with an unknown type", () => {
 | 
						|
        // @ts-ignore
 | 
						|
        renderPill({ type: "unknown" });
 | 
						|
        expect(renderResult.asFragment()).toMatchInlineSnapshot(`
 | 
						|
            <DocumentFragment>
 | 
						|
              <div />
 | 
						|
            </DocumentFragment>
 | 
						|
        `);
 | 
						|
    });
 | 
						|
});
 |