2023-03-08 12:06:50 +00:00
|
|
|
/*
|
2024-09-09 13:57:16 +00:00
|
|
|
Copyright 2024 New Vector Ltd.
|
2023-03-08 12:06:50 +00:00
|
|
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
|
|
|
2024-09-09 13:57:16 +00:00
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|
|
|
Please see LICENSE files in the repository root for full details.
|
2023-03-08 12:06:50 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
import React from "react";
|
|
|
|
import { act, render, RenderResult, screen } from "@testing-library/react";
|
|
|
|
import userEvent from "@testing-library/user-event";
|
|
|
|
import { mocked, Mocked } from "jest-mock";
|
2023-03-21 09:23:20 +00:00
|
|
|
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
2023-03-08 12:06:50 +00:00
|
|
|
|
|
|
|
import dis from "../../../../src/dispatcher/dispatcher";
|
|
|
|
import { Pill, PillProps, PillType } from "../../../../src/components/views/elements/Pill";
|
|
|
|
import {
|
|
|
|
filterConsole,
|
|
|
|
flushPromises,
|
2023-03-21 09:23:20 +00:00
|
|
|
mkMessage,
|
2023-03-08 12:06:50 +00:00
|
|
|
mkRoomCanonicalAliasEvent,
|
|
|
|
mkRoomMemberJoinEvent,
|
|
|
|
stubClient,
|
|
|
|
} from "../../../test-utils";
|
|
|
|
import DMRoomMap from "../../../../src/utils/DMRoomMap";
|
|
|
|
import { Action } from "../../../../src/dispatcher/actions";
|
2023-03-13 14:43:13 +00:00
|
|
|
import { ButtonEvent } from "../../../../src/components/views/elements/AccessibleButton";
|
2023-03-27 08:07:43 +00:00
|
|
|
import { SdkContextClass } from "../../../../src/contexts/SDKContext";
|
2023-03-08 12:06:50 +00:00
|
|
|
|
|
|
|
describe("<Pill>", () => {
|
|
|
|
let client: Mocked<MatrixClient>;
|
|
|
|
const permalinkPrefix = "https://matrix.to/#/";
|
|
|
|
const room1Alias = "#room1:example.com";
|
|
|
|
const room1Id = "!room1:example.com";
|
|
|
|
let room1: Room;
|
2023-03-21 09:23:20 +00:00
|
|
|
let room1Message: MatrixEvent;
|
|
|
|
const room2Id = "!room2:example.com";
|
|
|
|
let room2: Room;
|
2023-03-09 11:48:36 +00:00
|
|
|
const space1Id = "!space1:example.com";
|
|
|
|
let space1: Room;
|
2023-03-08 12:06:50 +00:00
|
|
|
const user1Id = "@user1:example.com";
|
|
|
|
const user2Id = "@user2:example.com";
|
2023-03-27 08:07:43 +00:00
|
|
|
const user3Id = "@user3:example.com";
|
2023-03-08 12:06:50 +00:00
|
|
|
let renderResult: RenderResult;
|
2023-03-13 14:43:13 +00:00
|
|
|
let pillParentClickHandler: (e: ButtonEvent) => void;
|
2023-03-08 12:06:50 +00:00
|
|
|
|
|
|
|
const renderPill = (props: PillProps): void => {
|
|
|
|
const withDefault = {
|
|
|
|
inMessage: true,
|
|
|
|
shouldShowPillAvatar: true,
|
|
|
|
...props,
|
|
|
|
} as PillProps;
|
2023-03-13 14:43:13 +00:00
|
|
|
// wrap Pill with a div to allow testing of event bubbling
|
|
|
|
renderResult = render(
|
2023-08-09 06:27:31 +00:00
|
|
|
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
|
2023-03-13 14:43:13 +00:00
|
|
|
<div onClick={pillParentClickHandler}>
|
|
|
|
<Pill {...withDefault} />
|
|
|
|
</div>,
|
|
|
|
);
|
2023-03-08 12:06:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
filterConsole(
|
|
|
|
"Failed to parse permalink Error: Unknown entity type in permalink",
|
|
|
|
"Room !room1:example.com does not have an m.room.create event",
|
2023-03-21 09:23:20 +00:00
|
|
|
"Room !space1:example.com does not have an m.room.create event",
|
2023-03-08 12:06:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
client = mocked(stubClient());
|
2023-03-27 08:07:43 +00:00
|
|
|
SdkContextClass.instance.client = client;
|
2023-05-23 15:24:12 +00:00
|
|
|
DMRoomMap.makeShared(client);
|
2023-03-21 09:23:20 +00:00
|
|
|
room1 = new Room(room1Id, client, user1Id);
|
2023-03-08 12:06:50 +00:00
|
|
|
room1.name = "Room 1";
|
|
|
|
const user1JoinRoom1Event = mkRoomMemberJoinEvent(user1Id, room1Id, {
|
|
|
|
displayname: "User 1",
|
|
|
|
});
|
|
|
|
room1.currentState.setStateEvents([
|
2023-03-21 09:23:20 +00:00
|
|
|
mkRoomCanonicalAliasEvent(user1Id, room1Id, room1Alias),
|
2023-03-08 12:06:50 +00:00
|
|
|
user1JoinRoom1Event,
|
|
|
|
]);
|
|
|
|
room1.getMember(user1Id)!.setMembershipEvent(user1JoinRoom1Event);
|
2023-03-21 09:23:20 +00:00
|
|
|
room1Message = mkMessage({
|
|
|
|
id: "$123-456",
|
|
|
|
event: true,
|
|
|
|
user: user1Id,
|
|
|
|
room: room1Id,
|
|
|
|
msg: "Room 1 Message",
|
|
|
|
});
|
|
|
|
room1.addLiveEvents([room1Message]);
|
|
|
|
|
|
|
|
room2 = new Room(room2Id, client, user1Id);
|
2023-03-27 08:07:43 +00:00
|
|
|
room2.currentState.setStateEvents([mkRoomMemberJoinEvent(user2Id, room2Id)]);
|
2023-03-21 09:23:20 +00:00
|
|
|
room2.name = "Room 2";
|
2023-03-08 12:06:50 +00:00
|
|
|
|
2023-03-09 11:48:36 +00:00
|
|
|
space1 = new Room(space1Id, client, client.getSafeUserId());
|
|
|
|
space1.name = "Space 1";
|
|
|
|
|
2023-03-27 08:07:43 +00:00
|
|
|
client.getRooms.mockReturnValue([room1, room2, space1]);
|
2023-03-08 12:06:50 +00:00
|
|
|
client.getRoom.mockImplementation((roomId: string) => {
|
|
|
|
if (roomId === room1.roomId) return room1;
|
2023-03-21 09:23:20 +00:00
|
|
|
if (roomId === room2.roomId) return room2;
|
2023-03-09 11:48:36 +00:00
|
|
|
if (roomId === space1.roomId) return space1;
|
2023-03-08 12:06:50 +00:00
|
|
|
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");
|
2023-03-13 14:43:13 +00:00
|
|
|
pillParentClickHandler = jest.fn();
|
2023-05-05 08:26:11 +00:00
|
|
|
|
|
|
|
jest.spyOn(global.Math, "random").mockReturnValue(0.123456);
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
jest.spyOn(global.Math, "random").mockRestore();
|
2023-03-08 12:06:50 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
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"));
|
|
|
|
});
|
|
|
|
|
2023-12-21 15:21:41 +00:00
|
|
|
it("should show a tooltip with the room Id", async () => {
|
|
|
|
expect(await screen.findByRole("tooltip", { name: room1Id })).toBeInTheDocument();
|
2023-03-08 12:06:50 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
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();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2023-03-09 11:48:36 +00:00
|
|
|
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();
|
|
|
|
});
|
|
|
|
|
2023-03-08 12:06:50 +00:00
|
|
|
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"));
|
|
|
|
});
|
|
|
|
|
2023-03-13 14:43:13 +00:00
|
|
|
it("should dipsatch a view user action and prevent event bubbling", () => {
|
2023-03-08 12:06:50 +00:00
|
|
|
expect(dis.dispatch).toHaveBeenCalledWith({
|
|
|
|
action: Action.ViewUser,
|
|
|
|
member: room1.getMember(user1Id),
|
|
|
|
});
|
2023-03-13 14:43:13 +00:00
|
|
|
|
|
|
|
expect(pillParentClickHandler).not.toHaveBeenCalled();
|
2023-03-08 12:06:50 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2023-03-27 08:07:43 +00:00
|
|
|
it("should render the expected pill for a known user not in the room", async () => {
|
2023-03-08 12:06:50 +00:00
|
|
|
renderPill({
|
|
|
|
room: room1,
|
|
|
|
url: permalinkPrefix + user2Id,
|
|
|
|
});
|
|
|
|
|
|
|
|
// wait for profile query via API
|
|
|
|
await act(async () => {
|
|
|
|
await flushPromises();
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(renderResult.asFragment()).toMatchSnapshot();
|
|
|
|
});
|
|
|
|
|
2023-03-27 08:07:43 +00:00
|
|
|
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 act(async () => {
|
|
|
|
await flushPromises();
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(renderResult.asFragment()).toMatchSnapshot();
|
|
|
|
});
|
|
|
|
|
2023-03-08 12:06:50 +00:00
|
|
|
it("should not render anything if the type cannot be detected", () => {
|
|
|
|
renderPill({
|
|
|
|
url: permalinkPrefix,
|
|
|
|
});
|
2023-03-13 14:43:13 +00:00
|
|
|
expect(renderResult.asFragment()).toMatchInlineSnapshot(`
|
|
|
|
<DocumentFragment>
|
|
|
|
<div />
|
|
|
|
</DocumentFragment>
|
|
|
|
`);
|
2023-03-08 12:06:50 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
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();
|
|
|
|
});
|
2023-03-21 09:23:20 +00:00
|
|
|
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>
|
|
|
|
`);
|
|
|
|
});
|
2023-03-08 12:06:50 +00:00
|
|
|
});
|