Room list: add default sections (#32785)
* feat: add sections to RLSV3 * feat: add sections in vms * feat: add room list section labs flag * fix: wrong margin for room list item when in sections * feat: hide favourites and low priority filters * fix: crash when changing filter * feat: support sticky room in sections * test: update SC snapshot * test: update SC screenshot * test: update RLS tests * test: add tests to RoomListSectionHeaderViewModel * test: fix existing test in RoomListViewModel * test: add sections tests for RoomListViewModel * test: add e2e tests for sections * fix: incorrect selected room when expanding/collasping a section * fix: typo in `roomSkipList` * feat: use one skip list with all filters instead of one list by tag * chore: put back comment about `roomIndexInSection` * chore: add missing `readonly` * chore: add doc about possible undefined value for room item vm
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type Locator, type Page } from "@playwright/test";
|
||||
|
||||
import { expect, test } from "../../../element-web-test";
|
||||
|
||||
test.describe("Room list sections", () => {
|
||||
test.use({
|
||||
displayName: "Alice",
|
||||
labsFlags: ["feature_new_room_list", "feature_room_list_sections"],
|
||||
botCreateOpts: {
|
||||
displayName: "BotBob",
|
||||
autoAcceptInvites: true,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the room list
|
||||
* @param page
|
||||
*/
|
||||
function getRoomList(page: Page): Locator {
|
||||
return page.getByTestId("room-list");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the primary filters
|
||||
* @param page
|
||||
*/
|
||||
function getPrimaryFilters(page: Page): Locator {
|
||||
return page.getByTestId("primary-filters");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a section header toggle button by section name
|
||||
* @param page
|
||||
* @param sectionName The display name of the section (e.g. "Favourites", "Chats", "Low Priority")
|
||||
*/
|
||||
function getSectionHeader(page: Page, sectionName: string): Locator {
|
||||
return getRoomList(page).getByRole("gridcell", { name: `Toggle ${sectionName} section` });
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ page, app, user }) => {
|
||||
// The notification toast is displayed above the search section
|
||||
await app.closeNotificationToast();
|
||||
|
||||
// focus the user menu to avoid to have hover decoration
|
||||
await page.getByRole("button", { name: "User menu" }).focus();
|
||||
});
|
||||
|
||||
test.describe("Section rendering", () => {
|
||||
test.beforeEach(async ({ app, user }) => {
|
||||
// Create regular rooms
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await app.client.createRoom({ name: `room${i}` });
|
||||
}
|
||||
});
|
||||
|
||||
test("should render sections with correct rooms in each", { tag: "@screenshot" }, async ({ page, app }) => {
|
||||
// Create a favourite room
|
||||
const favouriteId = await app.client.createRoom({ name: "favourite room" });
|
||||
await app.client.evaluate(async (client, roomId) => {
|
||||
await client.setRoomTag(roomId, "m.favourite");
|
||||
}, favouriteId);
|
||||
|
||||
// Create a low priority room
|
||||
const lowPrioId = await app.client.createRoom({ name: "low prio room" });
|
||||
await app.client.evaluate(async (client, roomId) => {
|
||||
await client.setRoomTag(roomId, "m.lowpriority");
|
||||
}, lowPrioId);
|
||||
|
||||
const roomList = getRoomList(page);
|
||||
|
||||
// All three section headers should be visible
|
||||
await expect(getSectionHeader(page, "Favourites")).toBeVisible();
|
||||
await expect(getSectionHeader(page, "Chats")).toBeVisible();
|
||||
await expect(getSectionHeader(page, "Low Priority")).toBeVisible();
|
||||
|
||||
// Ensure all rooms are visible
|
||||
await expect(roomList.getByRole("row", { name: "Open room favourite room" })).toBeVisible();
|
||||
await expect(roomList.getByRole("row", { name: "Open room low prio room" })).toBeVisible();
|
||||
await expect(roomList.getByRole("row", { name: "Open room room0" })).toBeVisible();
|
||||
|
||||
await expect(roomList).toMatchScreenshot("room-list-sections.png");
|
||||
});
|
||||
|
||||
test("should only show non-empty sections", async ({ page, app }) => {
|
||||
// No low priority rooms created, only regular and favourite rooms
|
||||
const favouriteId = await app.client.createRoom({ name: "favourite room" });
|
||||
await app.client.evaluate(async (client, roomId) => {
|
||||
await client.setRoomTag(roomId, "m.favourite");
|
||||
}, favouriteId);
|
||||
|
||||
// Chats and Favourites sections should still be visible
|
||||
await expect(getSectionHeader(page, "Chats")).toBeVisible();
|
||||
await expect(getSectionHeader(page, "Favourites")).toBeVisible();
|
||||
// Low Priority sections should not be visible
|
||||
await expect(getSectionHeader(page, "Low Priority")).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("should render a flat list when there is only rooms in Chats section", async ({ page, app }) => {
|
||||
// All sections should not be visible
|
||||
await expect(getSectionHeader(page, "Chats")).not.toBeVisible();
|
||||
await expect(getSectionHeader(page, "Favourites")).not.toBeVisible();
|
||||
await expect(getSectionHeader(page, "Low Priority")).not.toBeVisible();
|
||||
// It should be a flat list (using listbox a11y role)
|
||||
await expect(page.getByRole("listbox", { name: "Room list", exact: true })).toBeVisible();
|
||||
await expect(getRoomList(page).getByRole("option", { name: "Open room room0" })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Section collapse and expand", () => {
|
||||
[
|
||||
{ section: "Favourites", roomName: "favourite room", tag: "m.favourite" },
|
||||
{ section: "Low Priority", roomName: "low prio room", tag: "m.lowpriority" },
|
||||
].forEach(({ section, roomName, tag }) => {
|
||||
test(`should collapse and expand the ${section} section`, async ({ page, app }) => {
|
||||
const roomId = await app.client.createRoom({ name: roomName });
|
||||
if (tag) {
|
||||
await app.client.evaluate(
|
||||
async (client, { roomId, tag }) => {
|
||||
await client.setRoomTag(roomId, tag);
|
||||
},
|
||||
{ roomId, tag },
|
||||
);
|
||||
}
|
||||
|
||||
const roomList = getRoomList(page);
|
||||
const sectionHeader = getSectionHeader(page, section);
|
||||
|
||||
// The room should be visible
|
||||
await expect(roomList.getByRole("row", { name: `Open room ${roomName}` })).toBeVisible();
|
||||
|
||||
// Collapse the section
|
||||
await sectionHeader.click();
|
||||
|
||||
// The room should no longer be visible
|
||||
await expect(roomList.getByRole("row", { name: `Open room ${roomName}` })).not.toBeVisible();
|
||||
|
||||
// The section header should still be visible
|
||||
await expect(sectionHeader).toBeVisible();
|
||||
|
||||
// Expand the section again
|
||||
await sectionHeader.click();
|
||||
|
||||
// The room should be visible again
|
||||
await expect(roomList.getByRole("row", { name: `Open room ${roomName}` })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test("should render collapsed section", { tag: "@screenshot" }, async ({ page, app }) => {
|
||||
const favouriteId = await app.client.createRoom({ name: "favourite room" });
|
||||
await app.client.evaluate(async (client, roomId) => {
|
||||
await client.setRoomTag(roomId, "m.favourite");
|
||||
}, favouriteId);
|
||||
|
||||
await app.client.createRoom({ name: "regular room" });
|
||||
|
||||
const roomList = getRoomList(page);
|
||||
|
||||
// Collapse the Favourites section
|
||||
await getSectionHeader(page, "Favourites").click();
|
||||
|
||||
// Verify favourite room is hidden but regular room is still visible
|
||||
await expect(roomList.getByRole("row", { name: "Open room favourite room" })).not.toBeVisible();
|
||||
await expect(roomList.getByRole("row", { name: "Open room regular room" })).toBeVisible();
|
||||
|
||||
await expect(roomList).toMatchScreenshot("room-list-sections-collapsed.png");
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Rooms placement in sections", () => {
|
||||
test("should move a room between sections when tags change", async ({ page, app }) => {
|
||||
await app.client.createRoom({ name: "my room" });
|
||||
|
||||
const roomList = getRoomList(page);
|
||||
|
||||
// Flat list because there is only rooms in the Chats section
|
||||
let roomItem = roomList.getByRole("option", { name: "Open room my room" });
|
||||
await expect(roomItem).toBeVisible();
|
||||
|
||||
// Favourite the room via context menu
|
||||
await roomItem.click({ button: "right" });
|
||||
await page.getByRole("menuitemcheckbox", { name: "Favourited" }).click();
|
||||
|
||||
// The Favourites section header should now be visible and the room should be under it
|
||||
await expect(getSectionHeader(page, "Favourites")).toBeVisible();
|
||||
roomItem = roomList.getByRole("row", { name: "Open room my room" });
|
||||
await expect(roomItem).toBeVisible();
|
||||
|
||||
// Unfavourite the room
|
||||
await roomItem.hover();
|
||||
await roomItem.getByRole("button", { name: "More Options" }).click();
|
||||
await page.getByRole("menuitemcheckbox", { name: "Favourited" }).click();
|
||||
|
||||
// Mark the room as low priority via context menu
|
||||
roomItem = roomList.getByRole("option", { name: "Open room my room" });
|
||||
await roomItem.click({ button: "right" });
|
||||
await page.getByRole("menuitemcheckbox", { name: "Low priority" }).click();
|
||||
|
||||
// The Low Priority section header should now be visible and the room should be under it
|
||||
await expect(getSectionHeader(page, "Low Priority")).toBeVisible();
|
||||
roomItem = roomList.getByRole("row", { name: "Open room my room" });
|
||||
await expect(roomItem).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Sections and filters interaction", () => {
|
||||
test("should not show Favourite and Low Priority filters when sections are enabled", async ({ page, app }) => {
|
||||
const primaryFilters = getPrimaryFilters(page);
|
||||
|
||||
// Expand the filter list to see all filters
|
||||
const expandButton = primaryFilters.getByRole("button", { name: "Expand filter list" });
|
||||
await expandButton.click();
|
||||
|
||||
// Favourite and Low Priority filters should NOT be visible since sections handle them
|
||||
await expect(primaryFilters.getByRole("option", { name: "Favourite" })).not.toBeVisible();
|
||||
|
||||
// Other filters should still be present
|
||||
await expect(primaryFilters.getByRole("option", { name: "People" })).toBeVisible();
|
||||
await expect(primaryFilters.getByRole("option", { name: "Rooms" })).toBeVisible();
|
||||
await expect(primaryFilters.getByRole("option", { name: "Unread" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("should maintain sections when a filter is applied", async ({ page, app, bot }) => {
|
||||
// Create a favourite room with unread messages
|
||||
const favouriteId = await app.client.createRoom({ name: "fav with unread" });
|
||||
await app.client.evaluate(async (client, roomId) => {
|
||||
await client.setRoomTag(roomId, "m.favourite");
|
||||
}, favouriteId);
|
||||
await app.client.inviteUser(favouriteId, bot.credentials.userId);
|
||||
await bot.joinRoom(favouriteId);
|
||||
await bot.sendMessage(favouriteId, "Hello from favourite!");
|
||||
|
||||
// Create a regular room with unread messages
|
||||
const regularId = await app.client.createRoom({ name: "regular with unread" });
|
||||
await app.client.inviteUser(regularId, bot.credentials.userId);
|
||||
await bot.joinRoom(regularId);
|
||||
await bot.sendMessage(regularId, "Hello from regular!");
|
||||
|
||||
// Create a room without unread
|
||||
await app.client.createRoom({ name: "no unread room" });
|
||||
|
||||
const roomList = getRoomList(page);
|
||||
const primaryFilters = getPrimaryFilters(page);
|
||||
|
||||
// Apply the Unread filter
|
||||
await primaryFilters.getByRole("option", { name: "Unread" }).click();
|
||||
|
||||
// Only rooms with unreads should be visible
|
||||
await expect(roomList.getByRole("row", { name: "fav with unread" })).toBeVisible();
|
||||
await expect(roomList.getByRole("row", { name: "regular with unread" })).toBeVisible();
|
||||
await expect(roomList.getByRole("row", { name: "no unread room" })).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user