Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit a38ffa6

Browse files
committed
Add tests for PinnedMessageBanner.tsx
1 parent 57676fa commit a38ffa6

File tree

3 files changed

+401
-1
lines changed

3 files changed

+401
-1
lines changed

src/components/views/rooms/PinnedMessageBanner.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export function PinnedMessageBanner({ room, permalinkCreator }: PinnedMessageBan
108108
{!isSinglePinnedEvent && <Indicators count={eventCount} currentIndex={currentEventIndex} />}
109109
<PinIcon width="20" className="mx_PinnedMessageBanner_PinIcon" />
110110
{!isSinglePinnedEvent && (
111-
<div className="mx_PinnedMessageBanner_title">
111+
<div className="mx_PinnedMessageBanner_title" data-testid="banner-counter">
112112
{_t(
113113
"room|pinned_message_banner|title",
114114
{
@@ -203,6 +203,7 @@ interface IndicatorProps {
203203
function Indicator({ active, hided }: IndicatorProps): JSX.Element {
204204
return (
205205
<div
206+
data-testid="banner-indicator"
206207
className={classNames("mx_PinnedMessageBanner_Indicator", {
207208
"mx_PinnedMessageBanner_Indicator--active": active,
208209
"mx_PinnedMessageBanner_Indicator--hided": hided,
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
/*
2+
* Copyright 2024 The Matrix.org Foundation C.I.C.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { act, screen, render } from "@testing-library/react";
18+
import React from "react";
19+
import { EventType, IEvent, MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
20+
21+
import * as pinnedEventHooks from "../../../../src/hooks/usePinnedEvents";
22+
import { PinnedMessageBanner } from "../../../../src/components/views/rooms/PinnedMessageBanner";
23+
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
24+
import { stubClient } from "../../../test-utils";
25+
import dis from "../../../../src/dispatcher/dispatcher";
26+
import RightPanelStore from "../../../../src/stores/right-panel/RightPanelStore";
27+
import { RightPanelPhases } from "../../../../src/stores/right-panel/RightPanelStorePhases";
28+
import { UPDATE_EVENT } from "../../../../src/stores/AsyncStore";
29+
import { Action } from "../../../../src/dispatcher/actions";
30+
31+
describe("<PinnedMessageBanner />", () => {
32+
const userId = "@alice:server.org";
33+
const roomId = "!room:server.org";
34+
35+
let mockClient: MatrixClient;
36+
let room: Room;
37+
let permalinkCreator: RoomPermalinkCreator;
38+
beforeEach(() => {
39+
mockClient = stubClient();
40+
room = new Room(roomId, mockClient, userId);
41+
permalinkCreator = new RoomPermalinkCreator(room);
42+
jest.spyOn(dis, "dispatch").mockReturnValue(undefined);
43+
});
44+
45+
/**
46+
* Create a pinned event with the given content.
47+
* @param content
48+
*/
49+
function makePinEvent(content?: Partial<IEvent>) {
50+
return new MatrixEvent({
51+
type: EventType.RoomMessage,
52+
sender: userId,
53+
content: {
54+
body: "First pinned message",
55+
msgtype: "m.text",
56+
},
57+
room_id: roomId,
58+
origin_server_ts: 0,
59+
event_id: "$eventId",
60+
...content,
61+
});
62+
}
63+
64+
const event1 = makePinEvent();
65+
const event2 = makePinEvent({
66+
event_id: "$eventId2",
67+
content: { body: "Second pinned message" },
68+
});
69+
const event3 = makePinEvent({
70+
event_id: "$eventId3",
71+
content: { body: "Third pinned message" },
72+
});
73+
const event4 = makePinEvent({
74+
event_id: "$eventId4",
75+
content: { body: "Fourth pinned message" },
76+
});
77+
78+
/**
79+
* Render the banner
80+
*/
81+
function renderBanner() {
82+
return render(<PinnedMessageBanner permalinkCreator={permalinkCreator} room={room} />);
83+
}
84+
85+
it("should render nothing when there are no pinned events", async () => {
86+
jest.spyOn(pinnedEventHooks, "usePinnedEvents").mockReturnValue([]);
87+
const { container } = renderBanner();
88+
expect(container).toBeEmptyDOMElement();
89+
});
90+
91+
it("should render a single pinned event", async () => {
92+
jest.spyOn(pinnedEventHooks, "usePinnedEvents").mockReturnValue([event1.getId()!]);
93+
jest.spyOn(pinnedEventHooks, "useSortedFetchedPinnedEvents").mockReturnValue([event1]);
94+
95+
const { asFragment } = renderBanner();
96+
97+
expect(screen.getByText("First pinned message")).toBeVisible();
98+
expect(screen.queryByRole("button", { name: "View all" })).toBeNull();
99+
expect(asFragment()).toMatchSnapshot();
100+
});
101+
102+
it("should render 2 pinned event", async () => {
103+
jest.spyOn(pinnedEventHooks, "usePinnedEvents").mockReturnValue([event1.getId()!, event2.getId()!]);
104+
jest.spyOn(pinnedEventHooks, "useSortedFetchedPinnedEvents").mockReturnValue([event1, event2]);
105+
106+
const { asFragment } = renderBanner();
107+
108+
expect(screen.getByText("Second pinned message")).toBeVisible();
109+
expect(screen.getByTestId("banner-counter")).toHaveTextContent("2 of 2 Pinned messages");
110+
expect(screen.getAllByTestId("banner-indicator")).toHaveLength(2);
111+
expect(screen.queryByRole("button", { name: "View all" })).toBeVisible();
112+
expect(asFragment()).toMatchSnapshot();
113+
});
114+
115+
it("should render 4 pinned event", async () => {
116+
jest.spyOn(pinnedEventHooks, "usePinnedEvents").mockReturnValue([
117+
event1.getId()!,
118+
event2.getId()!,
119+
event3.getId()!,
120+
event4.getId()!,
121+
]);
122+
jest.spyOn(pinnedEventHooks, "useSortedFetchedPinnedEvents").mockReturnValue([event1, event2, event3, event4]);
123+
124+
const { asFragment } = renderBanner();
125+
126+
expect(screen.getByText("Fourth pinned message")).toBeVisible();
127+
expect(screen.getByTestId("banner-counter")).toHaveTextContent("4 of 4 Pinned messages");
128+
expect(screen.getAllByTestId("banner-indicator")).toHaveLength(3);
129+
expect(screen.queryByRole("button", { name: "View all" })).toBeVisible();
130+
expect(asFragment()).toMatchSnapshot();
131+
});
132+
133+
it("should rotate the pinned events when the banner is clicked", async () => {
134+
jest.spyOn(pinnedEventHooks, "usePinnedEvents").mockReturnValue([event1.getId()!, event2.getId()!]);
135+
jest.spyOn(pinnedEventHooks, "useSortedFetchedPinnedEvents").mockReturnValue([event1, event2]);
136+
137+
renderBanner();
138+
expect(screen.getByText("Second pinned message")).toBeVisible();
139+
140+
act(() => {
141+
screen.getByRole("button", { name: "View the pinned message in the timeline." }).click();
142+
});
143+
expect(screen.getByText("First pinned message")).toBeVisible();
144+
expect(screen.getByTestId("banner-counter")).toHaveTextContent("1 of 2 Pinned messages");
145+
expect(dis.dispatch).toHaveBeenCalledWith({
146+
action: Action.ViewRoom,
147+
event_id: event2.getId(),
148+
highlighted: true,
149+
room_id: room.roomId,
150+
metricsTrigger: undefined, // room doesn't change
151+
});
152+
153+
act(() => {
154+
screen.getByRole("button", { name: "View the pinned message in the timeline." }).click();
155+
});
156+
expect(screen.getByText("Second pinned message")).toBeVisible();
157+
expect(screen.getByTestId("banner-counter")).toHaveTextContent("2 of 2 Pinned messages");
158+
expect(dis.dispatch).toHaveBeenCalledWith({
159+
action: Action.ViewRoom,
160+
event_id: event1.getId(),
161+
highlighted: true,
162+
room_id: room.roomId,
163+
metricsTrigger: undefined, // room doesn't change
164+
});
165+
});
166+
167+
describe("Right button", () => {
168+
beforeEach(() => {
169+
jest.spyOn(pinnedEventHooks, "usePinnedEvents").mockReturnValue([event1.getId()!, event2.getId()!]);
170+
jest.spyOn(pinnedEventHooks, "useSortedFetchedPinnedEvents").mockReturnValue([event1, event2]);
171+
});
172+
173+
it("should display View all button if the right panel is closed", async () => {
174+
// The Right panel is closed
175+
jest.spyOn(RightPanelStore.instance, "isOpenForRoom").mockReturnValue(false);
176+
177+
renderBanner();
178+
expect(screen.getByRole("button", { name: "View all" })).toBeVisible();
179+
});
180+
181+
it("should display View all button if the right panel is not opened on the pinned message list", async () => {
182+
// The Right panel is opened on another card
183+
jest.spyOn(RightPanelStore.instance, "isOpenForRoom").mockReturnValue(true);
184+
jest.spyOn(RightPanelStore.instance, "currentCard", "get").mockReturnValue({
185+
phase: RightPanelPhases.RoomMemberList,
186+
});
187+
188+
renderBanner();
189+
expect(screen.getByRole("button", { name: "View all" })).toBeVisible();
190+
});
191+
192+
it("should display Close list button if the message pinning list is displayed", async () => {
193+
// The Right panel is closed
194+
jest.spyOn(RightPanelStore.instance, "isOpenForRoom").mockReturnValue(true);
195+
jest.spyOn(RightPanelStore.instance, "currentCard", "get").mockReturnValue({
196+
phase: RightPanelPhases.PinnedMessages,
197+
});
198+
199+
renderBanner();
200+
expect(screen.getByRole("button", { name: "Close list" })).toBeVisible();
201+
});
202+
203+
it("should open or close the message pinning list", async () => {
204+
// The Right panel is closed
205+
jest.spyOn(RightPanelStore.instance, "isOpenForRoom").mockReturnValue(true);
206+
jest.spyOn(RightPanelStore.instance, "currentCard", "get").mockReturnValue({
207+
phase: RightPanelPhases.PinnedMessages,
208+
});
209+
jest.spyOn(RightPanelStore.instance, "showOrHidePhase").mockReturnValue();
210+
211+
renderBanner();
212+
act(() => screen.getByRole("button", { name: "Close list" }).click());
213+
expect(RightPanelStore.instance.showOrHidePhase).toHaveBeenCalledWith(RightPanelPhases.PinnedMessages);
214+
});
215+
216+
it("should listen to the right panel", async () => {
217+
// The Right panel is closed
218+
jest.spyOn(RightPanelStore.instance, "isOpenForRoom").mockReturnValue(true);
219+
jest.spyOn(RightPanelStore.instance, "currentCard", "get").mockReturnValue({
220+
phase: RightPanelPhases.PinnedMessages,
221+
});
222+
223+
renderBanner();
224+
expect(screen.getByRole("button", { name: "Close list" })).toBeVisible();
225+
226+
jest.spyOn(RightPanelStore.instance, "isOpenForRoom").mockReturnValue(false);
227+
act(() => {
228+
RightPanelStore.instance.emit(UPDATE_EVENT);
229+
});
230+
expect(screen.getByRole("button", { name: "View all" })).toBeVisible();
231+
});
232+
});
233+
});

0 commit comments

Comments
 (0)