Skip to content

Commit 7f7a0d2

Browse files
committed
test: add tests for primary filters
1 parent bda136a commit 7f7a0d2

File tree

2 files changed

+197
-46
lines changed

2 files changed

+197
-46
lines changed

test/unit-tests/components/views/rooms/RoomListPanel/RoomListPrimaryFilters-test.tsx

Lines changed: 136 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,162 @@
55
* Please see LICENSE files in the repository root for full details.
66
*/
77

8-
import React from "react";
9-
import { render, screen } from "jest-matrix-react";
8+
import React, { act } from "react";
9+
import { render, screen, waitFor } from "jest-matrix-react";
1010
import userEvent from "@testing-library/user-event";
1111

1212
import { type RoomListViewState } from "../../../../../../src/components/viewmodels/roomlist/RoomListViewModel";
1313
import { RoomListPrimaryFilters } from "../../../../../../src/components/views/rooms/RoomListPanel/RoomListPrimaryFilters";
1414
import { FilterKey } from "../../../../../../src/stores/room-list-v3/skip-list/filters";
1515

16+
// Mock the useIsNodeVisible hook
17+
jest.mock("../../../../../../src/hooks/useIsNodeVisible", () => ({
18+
useIsNodeVisible: jest.fn().mockImplementation(() => ({
19+
isVisible: true,
20+
nodeRef: jest.fn(),
21+
rootRef: jest.fn(),
22+
})),
23+
}));
24+
1625
describe("<RoomListPrimaryFilters />", () => {
1726
let vm: RoomListViewState;
27+
const filterToggleMocks = [jest.fn(), jest.fn(), jest.fn()];
28+
29+
let resizeCallback: ResizeObserverCallback;
1830

1931
beforeEach(() => {
32+
// Reset mocks between tests
33+
filterToggleMocks.forEach((mock) => mock.mockClear());
34+
35+
// Mock ResizeObserver
36+
global.ResizeObserver = jest.fn().mockImplementation((callback) => {
37+
resizeCallback = callback;
38+
return {
39+
observe: jest.fn(),
40+
unobserve: jest.fn(),
41+
disconnect: jest.fn(),
42+
};
43+
});
44+
2045
vm = {
21-
isLoadingRooms: false,
22-
rooms: [],
23-
canCreateRoom: true,
24-
createRoom: jest.fn(),
25-
createChatRoom: jest.fn(),
2646
primaryFilters: [
27-
{ name: "People", active: false, toggle: jest.fn(), key: FilterKey.PeopleFilter },
28-
{ name: "Rooms", active: true, toggle: jest.fn(), key: FilterKey.RoomsFilter },
47+
{ name: "People", active: false, toggle: filterToggleMocks[0], key: FilterKey.PeopleFilter },
48+
{ name: "Rooms", active: true, toggle: filterToggleMocks[1], key: FilterKey.RoomsFilter },
49+
{ name: "Unreads", active: false, toggle: filterToggleMocks[2], key: FilterKey.UnreadFilter },
2950
],
30-
activeIndex: undefined,
31-
};
51+
} as unknown as RoomListViewState;
3252
});
3353

34-
it("should render primary filters", async () => {
35-
const user = userEvent.setup();
36-
54+
it("should renders all filters correctly", () => {
3755
const { asFragment } = render(<RoomListPrimaryFilters vm={vm} />);
56+
57+
// Check that all filters are rendered
3858
expect(screen.getByRole("option", { name: "People" })).toBeInTheDocument();
59+
expect(screen.getByRole("option", { name: "Rooms" })).toBeInTheDocument();
60+
expect(screen.getByRole("option", { name: "Unreads" })).toBeInTheDocument();
61+
62+
// Check that the active filter is marked as selected
3963
expect(screen.getByRole("option", { name: "Rooms" })).toHaveAttribute("aria-selected", "true");
64+
expect(screen.getByRole("option", { name: "People" })).toHaveAttribute("aria-selected", "false");
65+
expect(screen.getByRole("option", { name: "Unreads" })).toHaveAttribute("aria-selected", "false");
66+
4067
expect(asFragment()).toMatchSnapshot();
68+
});
69+
70+
it("should call toggle function when a filter is clicked", async () => {
71+
const user = userEvent.setup();
72+
render(<RoomListPrimaryFilters vm={vm} />);
73+
74+
// Click on an inactive filter
75+
await user.click(screen.getByRole("button", { name: "People" }));
76+
77+
// Check that the toggle function was called
78+
expect(filterToggleMocks[0]).toHaveBeenCalledTimes(1);
79+
});
80+
81+
it("should show chevron and expands filters when there's overflow", async () => {
82+
const user = userEvent.setup();
83+
render(<RoomListPrimaryFilters vm={vm} />);
84+
85+
expect(screen.getByTestId("filter-container")).toHaveAttribute("data-expanded", "false");
86+
87+
// Force the overflow state by simulating ResizeObserver callback
88+
// Mock that the scrollHeight is greater than FILTER_HEIGHT (30)
89+
jest.spyOn(screen.getByRole("listbox", { name: "Room list filters" }), "scrollHeight", "get").mockReturnValue(
90+
50,
91+
);
92+
// @ts-ignore
93+
act(() => resizeCallback());
94+
95+
await waitFor(async () => {
96+
const chevronButton = screen.getByRole("button", { name: "Expand filter list" });
97+
await user.click(chevronButton);
98+
});
99+
100+
// Check that the container has the expanded attribute
101+
await waitFor(() => {
102+
expect(screen.getByTestId("filter-container")).toHaveAttribute("data-expanded", "true");
103+
expect(screen.getByRole("button", { name: "Collapse filter list" })).toBeInTheDocument();
104+
});
105+
});
106+
107+
it("should not show the chevron when the list is equal at FILTER_HEIGHT", async () => {
108+
render(<RoomListPrimaryFilters vm={vm} />);
109+
110+
// Force the overflow state by simulating ResizeObserver callback
111+
// Mock that the scrollHeight is greater than FILTER_HEIGHT (30)
112+
jest.spyOn(screen.getByRole("listbox", { name: "Room list filters" }), "scrollHeight", "get").mockReturnValue(
113+
30,
114+
);
115+
116+
// @ts-ignore
117+
act(() => resizeCallback());
118+
119+
expect(screen.queryByRole("button", { name: "Expand filter list" })).toBeNull();
120+
});
121+
122+
it("handles filter reordering when active filter is not visible", async () => {
123+
// Mock useIsNodeVisible to return false, indicating active filter is not visible
124+
jest.requireMock("../../../../../../src/hooks/useIsNodeVisible").useIsNodeVisible.mockImplementation(() => ({
125+
isVisible: false,
126+
nodeRef: jest.fn(),
127+
rootRef: jest.fn(),
128+
}));
129+
130+
render(<RoomListPrimaryFilters vm={vm} />);
131+
132+
// Check that filters are reordered with the active one first
133+
const options = screen.getAllByRole("option");
134+
expect(options[0]).toHaveAttribute("aria-selected", "true");
135+
expect(options[0].textContent).toBe("Rooms");
136+
});
137+
138+
it("resets filter ordering when a filter is selected", async () => {
139+
// Setup: First mock it as not visible so it reorders
140+
jest.requireMock("../../../../../../src/hooks/useIsNodeVisible").useIsNodeVisible.mockImplementation(() => ({
141+
isVisible: false,
142+
nodeRef: jest.fn(),
143+
rootRef: jest.fn(),
144+
}));
145+
146+
const user = userEvent.setup();
147+
render(<RoomListPrimaryFilters vm={vm} />);
148+
149+
// Check initial order (reordered with active first)
150+
let options = screen.getAllByRole("option");
151+
expect(options[0].textContent).toBe("Rooms");
152+
153+
// Now change the hook to return true and click a filter
154+
jest.requireMock("../../../../../../src/hooks/useIsNodeVisible").useIsNodeVisible.mockImplementation(() => ({
155+
isVisible: true,
156+
nodeRef: jest.fn(),
157+
rootRef: jest.fn(),
158+
}));
41159

42160
await user.click(screen.getByRole("button", { name: "People" }));
43-
expect(vm.primaryFilters[0].toggle).toHaveBeenCalled();
161+
162+
// The order should be reset to original order
163+
options = screen.getAllByRole("option");
164+
expect(options[0].textContent).toBe("People");
44165
});
45166
});
Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,69 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`<RoomListPrimaryFilters /> should render primary filters 1`] = `
3+
exports[`<RoomListPrimaryFilters /> should renders all filters correctly 1`] = `
44
<DocumentFragment>
5-
<ul
6-
aria-label="Room list filters"
7-
class="mx_Flex mx_RoomListPrimaryFilters"
8-
role="listbox"
9-
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: wrap;"
5+
<div
6+
class="mx_RoomListPrimaryFilters"
107
>
11-
<li
12-
aria-selected="false"
13-
role="option"
8+
<div
9+
class="mx_RoomListPrimaryFilters_container"
10+
data-expanded="false"
11+
data-testid="filter-container"
12+
style="transition: 0.1s ease-in-out;"
1413
>
15-
<button
16-
aria-selected="false"
17-
class="_chat-filter_5qdp0_8"
18-
role="button"
19-
tabindex="0"
14+
<div
15+
class="mx_Flex mx_RoomListPrimaryFilters_animated"
16+
id="«r0»"
17+
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
2018
>
21-
People
22-
</button>
23-
</li>
24-
<li
25-
aria-selected="true"
26-
role="option"
27-
>
28-
<button
29-
aria-selected="true"
30-
class="_chat-filter_5qdp0_8"
31-
role="button"
32-
tabindex="0"
33-
>
34-
Rooms
35-
</button>
36-
</li>
37-
</ul>
19+
<ul
20+
aria-label="Room list filters"
21+
class="mx_Flex"
22+
role="listbox"
23+
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: wrap;"
24+
>
25+
<li
26+
aria-selected="false"
27+
role="option"
28+
>
29+
<button
30+
aria-selected="false"
31+
class="_chat-filter_5qdp0_8"
32+
role="button"
33+
tabindex="0"
34+
>
35+
People
36+
</button>
37+
</li>
38+
<li
39+
aria-selected="true"
40+
role="option"
41+
>
42+
<button
43+
aria-selected="true"
44+
class="_chat-filter_5qdp0_8"
45+
role="button"
46+
tabindex="0"
47+
>
48+
Rooms
49+
</button>
50+
</li>
51+
<li
52+
aria-selected="false"
53+
role="option"
54+
>
55+
<button
56+
aria-selected="false"
57+
class="_chat-filter_5qdp0_8"
58+
role="button"
59+
tabindex="0"
60+
>
61+
Unreads
62+
</button>
63+
</li>
64+
</ul>
65+
</div>
66+
</div>
67+
</div>
3868
</DocumentFragment>
3969
`;

0 commit comments

Comments
 (0)