Room list: move sections out of labs to all the users (#33810)

* feat: remove section labs flag

* test: update tests

* feat: remove favourites and low prioriy filter in SC

* test: update screenshots

* test: update snapshots

* test: update playwright tests

* test: update playright screenshots

* test: update screenshot

* test: close release announcement

* test: fix skeleton screenshot

* test: fix sliding sync

* test: update room list tests

* test: update room list tests after section dnd

* test: update toast screenshot

* test: update again room list tests

* test: update screenshot
This commit is contained in:
Florian Duros
2026-06-29 16:30:27 +00:00
committed by GitHub
parent 219ca4a3e9
commit aed2009b9f
85 changed files with 131 additions and 846 deletions
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { createNewInstance, rejectToast } from "@element-hq/element-web-playwright-common";
import { closeReleaseAnnouncement, createNewInstance, rejectToast } from "@element-hq/element-web-playwright-common";
import { expect, test } from "../../element-web-test";
import { ElementAppPage } from "../../pages/ElementAppPage";
@@ -30,6 +30,11 @@ test.describe("History sharing", function () {
await aliceElementApp.client.bootstrapCrossSigning(aliceCredentials);
await aliceElementApp.closeKeyStorageToast();
await rejectToast(alicePage, "Notifications");
// Close the release announcement about the new room list sections
await closeReleaseAnnouncement(alicePage, "Introducing Sections");
// Register a second user, and open it in a second instance of the app
const bobCredentials = await homeserver.registerUser(`user_${testInfo.testId}_bob`, "password", "Bob");
const bobPage = await createNewInstance(browser, bobCredentials, {}, labsFlags);
@@ -37,8 +42,6 @@ test.describe("History sharing", function () {
await bobElementApp.client.bootstrapCrossSigning(bobCredentials);
await bobElementApp.closeKeyStorageToast();
await rejectToast(aliceElementApp.page, "Notifications");
// Create the room and send a message
await createRoom(alicePage, "TestRoom", true);
@@ -6,6 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { closeReleaseAnnouncement } from "@element-hq/element-web-playwright-common";
import type { Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
import { openIntegrationManager } from "./utils";
@@ -139,6 +141,11 @@ test.describe("Integration Manager: Kick", () => {
await app.viewRoomByName(ROOM_NAME);
});
test.beforeEach(async ({ page, user, app, room }) => {
// Close the release announcement about the new room list sections
await closeReleaseAnnouncement(page, "Introducing Sections");
});
test("should kick the target", async ({ page, app, bot: targetUser, room }) => {
await app.viewRoomByName(ROOM_NAME);
await app.client.inviteUser(room.roomId, targetUser.credentials.userId);
@@ -6,6 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { closeReleaseAnnouncement, rejectToast } from "@element-hq/element-web-playwright-common";
import type { Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
import { openIntegrationManager } from "./utils";
@@ -103,6 +105,11 @@ test.describe("Integration Manager: Send Event", () => {
});
test.beforeEach(async ({ page, user, app, room }) => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
// Close the release announcement about the new room list sections
await closeReleaseAnnouncement(page, "Introducing Sections");
await app.client.setAccountData("m.widgets", {
"m.integration_manager": {
content: {
@@ -22,7 +22,7 @@ import {
test.describe("Room list custom sections", () => {
test.use({
displayName: "Alice",
labsFlags: ["feature_new_room_list", "feature_room_list_sections"],
labsFlags: ["feature_new_room_list"],
botCreateOpts: {
displayName: "BotBob",
autoAcceptInvites: true,
@@ -7,7 +7,7 @@
import { type Visibility } from "matrix-js-sdk/src/matrix";
import { type Page } from "@playwright/test";
import { rejectToast } from "@element-hq/element-web-playwright-common";
import { closeReleaseAnnouncement, rejectToast } from "@element-hq/element-web-playwright-common";
import { expect, test } from "../../../element-web-test";
import { SettingLevel } from "../../../../src/settings/SettingLevel";
@@ -35,6 +35,9 @@ test.describe("Room list filters and sort", () => {
// The toasts are displayed above the search section
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
// Close the release announcement about the new room list sections
await closeReleaseAnnouncement(page, "Introducing Sections");
});
test("Tombstoned rooms are not shown even when they receive updates", async ({ page, app, bot }) => {
@@ -177,19 +180,19 @@ test.describe("Room list filters and sort", () => {
await expect.poll(() => roomList.locator("role=option").count()).toBe(2);
await primaryFilters.getByRole("option", { name: "Rooms" }).click();
await expect(roomList.getByRole("option", { name: "unread room" })).toBeVisible();
await expect(roomList.getByRole("option", { name: "favourite room" })).toBeVisible();
await expect(roomList.getByRole("option", { name: "empty room" })).toBeVisible();
await expect(roomList.getByRole("option", { name: "room with mention" })).toBeVisible();
await expect(roomList.getByRole("option", { name: "Low prio room" })).toBeVisible();
await expect.poll(() => roomList.locator("role=option").count()).toBe(5);
// "Open room" prefix disambiguates the room tile from the "Toggle Chats section with
// unread rooms" section header button, which also matches the "unread room" substring.
await expect(roomList.getByRole("button", { name: "Open room unread room" })).toBeVisible();
await expect(roomList.getByRole("button", { name: "favourite room" })).toBeVisible();
await expect(roomList.getByRole("button", { name: "empty room" })).toBeVisible();
await expect(roomList.getByRole("button", { name: "room with mention" })).toBeVisible();
await expect(roomList.getByRole("button", { name: "Low prio room" })).toBeVisible();
// 5 room tiles spread across 3 sections (Favourites, Rooms, Low priority); each section
// header is also a button, so 5 rooms + 3 section headers = 8 buttons.
await expect.poll(() => roomList.locator("role=button").count()).toBe(8);
await getFilterExpandButton(page).click();
await primaryFilters.getByRole("option", { name: "Favourite" }).click();
await expect(roomList.getByRole("option", { name: "favourite room" })).toBeVisible();
await expect.poll(() => roomList.locator("role=option").count()).toBe(1);
await primaryFilters.getByRole("option", { name: "Mentions" }).click();
await expect(roomList.getByRole("option", { name: "room with mention" })).toBeVisible();
await expect.poll(() => roomList.locator("role=option").count()).toBe(1);
@@ -216,7 +219,7 @@ test.describe("Room list filters and sort", () => {
await app.settings.closeDialog();
// Let's open a room other than unread room or unread dm
await roomListView.getByRole("option", { name: "Open room favourite room" }).click();
await roomListView.getByRole("button", { name: "Open room favourite room" }).click();
// Let's make the bot send a new message in both rooms
await bot.sendMessage(unReadDmId, "Hello!");
@@ -239,15 +242,20 @@ test.describe("Room list filters and sort", () => {
await getRoomOptionsMenu(page).click();
await page.getByRole("menuitemradio", { name: "A-Z" }).click();
await expect(roomListView.getByRole("option").first()).toHaveText(/empty room/);
// Favourite + chat section headers are buttons + favourite room
await expect(roomListView.getByRole("button").nth(3)).toHaveText(/empty room/);
});
test("should move room to the top on message when sorting by activity", async ({ page, bot }) => {
test("should move room to the top on message (chat section) when sorting by activity", async ({
page,
bot,
}) => {
const roomListView = getRoomList(page);
await bot.sendMessage(unReadDmId, "Hello!");
await expect(roomListView.getByRole("option").first()).toHaveText(/unread dm/);
// Favourite + chat section headers are buttons + favourite room
await expect(roomListView.getByRole("button").nth(3)).toHaveText(/unread dm/);
});
});
@@ -302,7 +310,7 @@ test.describe("Room list filters and sort", () => {
);
});
["People", "Rooms", "Favourite"].forEach((filter) => {
["People", "Rooms"].forEach((filter) => {
test(
`should render the placeholder for ${filter} filter`,
{ tag: "@screenshot" },
@@ -5,7 +5,7 @@
* Please see LICENSE files in the repository root for full details.
*/
import { rejectToast } from "@element-hq/element-web-playwright-common";
import { closeReleaseAnnouncement, rejectToast } from "@element-hq/element-web-playwright-common";
import { test, expect } from "../../../element-web-test";
import { getHeaderSection } from "./utils";
@@ -19,6 +19,9 @@ test.describe("Header section of the room list", () => {
// The toasts are displayed above the search section
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
// Close the release announcement about the new room list sections
await closeReleaseAnnouncement(page, "Introducing Sections");
});
test("should render the header section", { tag: "@screenshot" }, async ({ page, app, user }) => {
@@ -13,7 +13,7 @@ import { assertRoomInSection, dragRoomToSection, getPrimaryFilters, getRoomList,
test.describe("Room list sections", () => {
test.use({
displayName: "Alice",
labsFlags: ["feature_new_room_list", "feature_room_list_sections"],
labsFlags: ["feature_new_room_list"],
botCreateOpts: {
displayName: "BotBob",
autoAcceptInvites: true,
@@ -6,7 +6,7 @@
*/
import { type Page } from "@playwright/test";
import { rejectToast } from "@element-hq/element-web-playwright-common";
import { closeReleaseAnnouncement, rejectToast } from "@element-hq/element-web-playwright-common";
import { expect, test } from "../../../element-web-test";
import { type Bot } from "../../../pages/bot";
@@ -27,6 +27,9 @@ test.describe("Room list", () => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
// Close the release announcement about the new room list sections
await closeReleaseAnnouncement(page, "Introducing Sections");
// focus the user menu to avoid to have hover decoration
await page.getByRole("button", { name: "User menu" }).focus();
});
@@ -71,11 +74,11 @@ test.describe("Room list", () => {
test("should open the more options menu", { tag: "@screenshot" }, async ({ page, app, user }) => {
const roomListView = getRoomList(page);
const roomItem = roomListView.getByRole("option", { name: "Open room room29" });
let roomItem = roomListView.getByRole("option", { name: "Open room room29" });
await roomItem.hover();
await expect(roomItem).toMatchScreenshot("room-list-item-hover.png");
const roomItemMenu = roomItem.getByRole("button", { name: "More Options" });
let roomItemMenu = roomItem.getByRole("button", { name: "More Options" });
await roomItemMenu.click();
await expect(page).toMatchScreenshot("room-list-item-open-more-options.png");
@@ -83,7 +86,9 @@ test.describe("Room list", () => {
await page.getByRole("menuitemcheckbox", { name: "Favourited" }).click();
// Check that the room is favourited
roomItem = roomListView.getByRole("gridcell", { name: "Open room room29" });
await roomItem.hover();
roomItemMenu = roomItem.getByRole("button", { name: "More Options" });
await roomItemMenu.click();
await expect(page.getByRole("menuitemcheckbox", { name: "Favourited" })).toBeChecked();
// It should show the invite dialog
@@ -296,13 +301,14 @@ test.describe("Room list", () => {
// @ts-ignore Visibility enum is not accessible
await app.client.createRoom({ name: "low priority room", visibility: "public" });
const roomListView = getRoomList(page);
const publicRoom = roomListView.getByRole("option", { name: "low priority room" });
let publicRoom = roomListView.getByRole("option", { name: "low priority room" });
// Make room low priority
await publicRoom.click({ button: "right" });
await page.getByRole("menuitemcheckbox", { name: "Low priority" }).click();
// Should have low priority decoration
publicRoom = roomListView.getByRole("gridcell", { name: "low priority room" });
await expect(publicRoom.locator(".mx_RoomAvatarView_icon")).toHaveAccessibleName(
"This is a low priority room",
);
@@ -6,6 +6,7 @@
*/
import { type Locator } from "@playwright/test";
import { closeReleaseAnnouncement, rejectToast } from "@element-hq/element-web-playwright-common";
import { test, expect } from "../../../element-web-test";
@@ -18,7 +19,12 @@ test.describe("Roles & Permissions room settings tab", () => {
let settings: Locator;
test.beforeEach(async ({ user, app }) => {
test.beforeEach(async ({ user, app, page }) => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
// Close the release announcement about the new room list sections
await closeReleaseAnnouncement(page, "Introducing Sections");
await app.client.createRoom({
name: roomName,
power_level_content_override: {
@@ -291,7 +291,7 @@ test.describe("Sliding Sync", () => {
await checkOrder(["Room to Join", "Test Room"], page);
});
test("should show a favourite DM only in the favourite sublist", async ({ page, app }) => {
test("should show a favourite DM only in the favourite section", async ({ page, app }) => {
const roomId = await app.client.createRoom({
name: "Favourite DM",
is_direct: true,
@@ -300,12 +300,9 @@ test.describe("Sliding Sync", () => {
await client.setRoomTag(roomId, "m.favourite", { order: 0.5 });
}, roomId);
await getFilterExpandButton(page).click();
await expect(page.getByRole("button", { name: "Favourite DM" })).toBeVisible();
const primaryFilters = getPrimaryFilters(page);
await primaryFilters.getByRole("option", { name: "Favourites" }).click();
await expect(page.getByRole("option", { name: "Favourite DM" })).toBeVisible();
await primaryFilters.getByRole("option", { name: "People" }).click();
await expect(page.getByRole("option", { name: "Favourite DM" })).not.toBeAttached();
@@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
import { readFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import { type Page } from "@playwright/test";
import { closeReleaseAnnouncement, rejectToast } from "@element-hq/element-web-playwright-common";
import type { EventType, Preset } from "matrix-js-sdk/src/matrix";
import { SettingLevel } from "../../../src/settings/SettingLevel";
@@ -132,6 +133,11 @@ test.describe("Element Call", () => {
SettingLevel.DEVICE,
new URL("/widget.html#", page.url()).toString(),
);
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
// Close the release announcement about the new room list sections
await closeReleaseAnnouncement(page, "Introducing Sections");
});
test.describe("Group Chat", () => {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

-1
View File
@@ -1582,7 +1582,6 @@
"notifications": "Enable the notifications panel in the room header",
"render_reaction_images": "Render custom images in reactions",
"render_reaction_images_description": "Sometimes referred to as \"custom emojis\".",
"room_list_sections": "Room list sections",
"sliding_sync": "Sliding Sync mode",
"sliding_sync_description": "Under active development, cannot be disabled. Currently, not compatible with Element Call.",
"sliding_sync_disabled_notice": "Sign in again to disable",
-10
View File
@@ -227,7 +227,6 @@ export interface Settings {
"feature_render_reaction_images": IFeature;
"feature_new_room_list": IFeature;
"feature_retention": IFeature;
"feature_room_list_sections": IFeature;
"feature_ask_to_join": IFeature;
"feature_notifications": IFeature;
"feature_msc4362_encrypted_state_events": IFeature;
@@ -665,15 +664,6 @@ export const SETTINGS: Settings = {
default: true,
controller: new ReloadOnChangeController(),
},
"feature_room_list_sections": {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
labsGroup: LabGroup.Ui,
displayName: _td("labs|room_list_sections"),
description: _td("labs|under_active_development"),
isFeature: true,
default: false,
controller: new ReloadOnChangeController(),
},
"feature_login_with_qr": {
supportedLevels: [SettingLevel.CONFIG],
labsGroup: LabGroup.Ui,
@@ -173,10 +173,7 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
public getSortedRoomsInActiveSpace(filterKeys?: FilterKey[]): RoomsResult {
const spaceId = SpaceStore.instance.activeSpace;
const areSectionsEnabled = SettingsStore.getValue("feature_room_list_sections");
const sections = areSectionsEnabled
? this.getSections(filterKeys)
: [{ tag: CHATS_TAG, rooms: Array.from(this.roomSkipList?.getRoomsInActiveSpace(filterKeys) ?? []) }];
const sections = this.getSections(filterKeys);
return {
spaceId: spaceId,
@@ -243,9 +243,7 @@ export class RoomListHeaderViewModel
};
public onReleaseAnnouncementChanged = (): void => {
const isSectionFeatureEnabled = SettingsStore.getValue("feature_room_list_sections");
const displaySectionReleaseAnnouncement =
isSectionFeatureEnabled &&
ReleaseAnnouncementStore.instance.getReleaseAnnouncement() === "room_list_section";
this.snapshot.merge({ displaySectionReleaseAnnouncement });
};
@@ -310,35 +308,27 @@ function computeHeaderSpaceState(
spaceStore: SpaceStoreClass,
matrixClient: MatrixClient,
): Omit<RoomListHeaderViewSnapshot, "activeSortOption" | "isMessagePreviewEnabled"> {
const isSectionFeatureEnabled = SettingsStore.getValue("feature_room_list_sections");
const displaySectionReleaseAnnouncement =
isSectionFeatureEnabled && ReleaseAnnouncementStore.instance.getReleaseAnnouncement() === "room_list_section";
ReleaseAnnouncementStore.instance.getReleaseAnnouncement() === "room_list_section";
const activeSpace = spaceStore.activeSpaceRoom;
const title = getHeaderTitle(spaceStore);
const canCreateRoom = hasCreateRoomRights(matrixClient, activeSpace);
const canCreateVideoRoom = getCanCreateVideoRoom(canCreateRoom);
const displayComposeMenu = isSectionFeatureEnabled || canCreateRoom;
const displaySpaceMenu = Boolean(activeSpace);
const canInviteInSpace = Boolean(
activeSpace?.getJoinRule() === JoinRule.Public || activeSpace?.canInvite(matrixClient.getSafeUserId()),
);
const canAccessSpaceSettings = Boolean(activeSpace && shouldShowSpaceSettings(activeSpace));
const useComposeIcon = !isSectionFeatureEnabled;
const canCreateSection = isSectionFeatureEnabled;
return {
title,
canCreateRoom,
canCreateVideoRoom,
displayComposeMenu,
displaySpaceMenu,
canInviteInSpace,
canAccessSpaceSettings,
canCreateSection,
useComposeIcon,
displaySectionReleaseAnnouncement,
};
}
@@ -288,10 +288,8 @@ export class RoomListItemViewModel
const callType =
call?.callType === CallType.Voice ? "voice" : call?.callType === CallType.Video ? "video" : undefined;
const canMoveToSection = SettingsStore.getValue("feature_room_list_sections");
// Build sections list for the "Move to section" submenu
const sections: Section[] = canMoveToSection ? RoomListItemViewModel.buildSections(roomTags) : [];
const sections: Section[] = RoomListItemViewModel.buildSections(roomTags);
return {
id: room.roomId,
@@ -320,7 +318,6 @@ export class RoomListItemViewModel
canMarkAsRead,
canMarkAsUnread,
roomNotifState,
canMoveToSection,
sections,
};
}
@@ -40,7 +40,6 @@ import { keepIfSame } from "../../utils/keepIfSame";
import { DefaultTagID } from "../../stores/room-list-v3/skip-list/tag";
import { RoomListSectionHeaderViewModel } from "./RoomListSectionHeaderViewModel";
import { getCustomSectionData, isCustomSectionTag, CHATS_TAG } from "../../stores/room-list-v3/section";
import SettingsStore from "../../settings/SettingsStore";
import { tagRoom } from "../../utils/room/tagRoom";
import { getSectionTagForRoom } from "../../utils/room/getSectionTagForRoom";
@@ -64,10 +63,8 @@ const filterKeyToIdMap: Map<FilterEnum, FilterId> = new Map([
[FilterEnum.UnreadFilter, "unread"],
[FilterEnum.PeopleFilter, "people"],
[FilterEnum.RoomsFilter, "rooms"],
[FilterEnum.FavouriteFilter, "favourite"],
[FilterEnum.MentionsFilter, "mentions"],
[FilterEnum.InvitesFilter, "invites"],
[FilterEnum.LowPriorityFilter, "low_priority"],
]);
const TAG_TO_TITLE_MAP: Record<string, string> = {
@@ -146,11 +143,7 @@ export class RoomListViewModel
const roomsResult = RoomListStoreV3.instance.getSortedRoomsInActiveSpace(undefined);
const canCreateRoom = hasCreateRoomRights(props.client, activeSpace);
// Remove favourite and low priority filters if sections are enabled, as they are redundant with the sections
const areSectionsEnabled = SettingsStore.getValue("feature_room_list_sections");
const filterIds = [...filterKeyToIdMap.values()].filter(
(id) => !areSectionsEnabled || (id !== "favourite" && id !== "low_priority"),
);
const filterIds = [...filterKeyToIdMap.values()];
// By default, all sections are expanded
const { sections, isFlatList } = computeSections(roomsResult, (tag) => true);
@@ -756,8 +749,8 @@ export class RoomListViewModel
* @param isFlatList - Whether the room list is currently displayed as a flat list
*/
private notifyCollapseState(isFlatList: boolean): void {
// Hide collapse/expand all button if sections are disabled or if it's a flat list
if (!SettingsStore.getValue("feature_room_list_sections") || isFlatList) {
// Hide collapse/expand all button if it's a flat list
if (isFlatList) {
dispatcher.dispatch<RoomListSectionsCollapseStateChangedPayload>({
action: Action.RoomListSectionsCollapseStateChanged,
collapseSections: undefined,
@@ -929,7 +929,6 @@ describe("RoomListStoreV3", () => {
describe("Sections", () => {
function enableSections(): void {
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting: string) => {
if (setting === "feature_room_list_sections") return true;
if (setting === "RoomList.OrderedCustomSections") return [];
if (setting === "RoomList.CustomSectionData") return {};
return false;
@@ -948,26 +947,7 @@ describe("RoomListStoreV3", () => {
return { client, rooms };
}
it("returns a single chats section when sections feature is disabled", async () => {
const { rooms } = getClientAndRooms();
// Mark some rooms as favourite so we can verify they are NOT split out
[0, 1, 2].forEach((i) => {
rooms[i].tags[DefaultTagID.Favourite] = {};
});
const store = new RoomListStoreV3Class(dispatcher);
await store.start();
const result = store.getSortedRoomsInActiveSpace();
expect(result.sections).toHaveLength(1);
expect(result.sections[0].tag).toBe(CHATS_TAG);
// All rooms, including favourites, are in the single section
for (const i of [0, 1, 2]) {
expect(result.sections[0].rooms).toContain(rooms[i]);
}
});
it("returns three sections in the correct order when enabled", async () => {
it("returns three sections in the correct order", async () => {
enableSections();
getClientAndRooms();
@@ -1229,7 +1209,6 @@ describe("RoomListStoreV3", () => {
const customTag = "element.io.section.custom";
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting: string) => {
if (setting === "feature_room_list_sections") return true;
if (setting === "RoomList.OrderedCustomSections") return [];
if (setting === "RoomList.CustomSectionData") return {};
return false;
@@ -1244,7 +1223,6 @@ describe("RoomListStoreV3", () => {
// Mark a room with the custom tag and update the settings
rooms[0].tags = { [customTag]: { order: 0 } };
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting: string) => {
if (setting === "feature_room_list_sections") return true;
if (setting === "RoomList.OrderedCustomSections") return [customTag];
if (setting === "RoomList.CustomSectionData")
return { [customTag]: { tag: customTag, name: "Custom" } };
@@ -86,7 +86,6 @@ describe("RoomListHeaderViewModel", () => {
const snapshot = vm.getSnapshot();
expect(snapshot.title).toBe("Home");
expect(snapshot.displayComposeMenu).toBe(true);
expect(snapshot.displaySpaceMenu).toBe(false);
expect(snapshot.canCreateRoom).toBe(true);
expect(snapshot.canCreateVideoRoom).toBe(true);
@@ -123,28 +122,6 @@ describe("RoomListHeaderViewModel", () => {
expect(vm.getSnapshot().activeSortOption).toBe("alphabetical");
});
it("should hide compose menu when user cannot create rooms", () => {
mocked(hasCreateRoomRights).mockReturnValue(false);
vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance });
const snapshot = vm.getSnapshot();
expect(snapshot.displayComposeMenu).toBe(false);
expect(snapshot.canCreateRoom).toBe(false);
});
it("should display compose menu when section feature is enabled@", () => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName: string) => {
if (settingName === "feature_room_list_sections") return true;
return false;
});
vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance });
const snapshot = vm.getSnapshot();
expect(snapshot.displayComposeMenu).toBe(true);
});
it("should show invite option when space is public", () => {
jest.spyOn(SpaceStore.instance, "activeSpace", "get").mockReturnValue(mockSpace.roomId);
jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(mockSpace);
@@ -179,28 +156,7 @@ describe("RoomListHeaderViewModel", () => {
expect(vm.getSnapshot().isMessagePreviewEnabled).toBe(true);
});
it.each([
[true, true, false],
[false, false, true],
])(
"when feature_room_list_sections is %s: canCreateSection=%s, useComposeIcon=%s",
(featureEnabled, expectedCanCreateSection, expectedUseComposeIcon) => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName: string) => {
if (settingName === "feature_room_list_sections") return featureEnabled;
return false;
});
vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance });
expect(vm.getSnapshot().canCreateSection).toBe(expectedCanCreateSection);
expect(vm.getSnapshot().useComposeIcon).toBe(expectedUseComposeIcon);
},
);
it("should set displaySectionReleaseAnnouncement to true when sections feature is enabled and announcement is active", () => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName: string) => {
if (settingName === "feature_room_list_sections") return true;
return false;
});
jest.spyOn(ReleaseAnnouncementStore.instance, "getReleaseAnnouncement").mockReturnValue(
"room_list_section",
);
@@ -208,15 +164,6 @@ describe("RoomListHeaderViewModel", () => {
vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance });
expect(vm.getSnapshot().displaySectionReleaseAnnouncement).toBe(true);
});
it("should set displaySectionReleaseAnnouncement to false when sections feature is disabled", () => {
jest.spyOn(ReleaseAnnouncementStore.instance, "getReleaseAnnouncement").mockReturnValue(
"room_list_section",
);
vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance });
expect(vm.getSnapshot().displaySectionReleaseAnnouncement).toBe(false);
});
});
describe("event listeners", () => {
@@ -509,20 +509,6 @@ describe("RoomListItemViewModel", () => {
});
});
describe("canMoveToSection", () => {
it.each([
[true, true],
[false, false],
])("should be %s when feature_room_list_sections is %s", (featureEnabled, expected) => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => {
if (setting === "feature_room_list_sections") return featureEnabled;
return false;
});
viewModel = new RoomListItemViewModel({ room, client: matrixClient });
expect(viewModel.getSnapshot().canMoveToSection).toBe(expected);
});
});
describe("Actions", () => {
it("should dispatch view room action on openRoom", () => {
viewModel = new RoomListItemViewModel({ room, client: matrixClient });
@@ -631,10 +617,6 @@ describe("RoomListItemViewModel", () => {
});
it("should include sections from orderedSectionTags excluding CHATS_TAG, favourite, and low priority", () => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => {
if (setting === "feature_room_list_sections") return true;
return false;
});
viewModel = new RoomListItemViewModel({ room, client: matrixClient });
const sections = viewModel.getSnapshot().sections;
@@ -643,10 +625,7 @@ describe("RoomListItemViewModel", () => {
it("should mark the room current section as selected", () => {
room.tags = { [customTag]: { order: 0 } };
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => {
if (setting === "feature_room_list_sections") return true;
return false;
});
viewModel = new RoomListItemViewModel({ room, client: matrixClient });
const sections = viewModel.getSnapshot().sections;
@@ -655,7 +634,6 @@ describe("RoomListItemViewModel", () => {
it("should use custom section name from CustomSectionData", () => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => {
if (setting === "feature_room_list_sections") return true;
if (setting === "RoomList.CustomSectionData")
return { [customTag]: { name: "My Custom Section", tag: customTag } };
return false;
@@ -672,10 +650,6 @@ describe("RoomListItemViewModel", () => {
if (setting === "RoomList.OrderedCustomSections") watchCallback = callback;
return "watcher-id";
});
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => {
if (setting === "feature_room_list_sections") return true;
return false;
});
viewModel = new RoomListItemViewModel({ room, client: matrixClient });
expect(viewModel.getSnapshot().sections).toHaveLength(1);
@@ -480,20 +480,6 @@ describe("RoomListViewModel", () => {
});
});
describe("notifyCollapseState", () => {
it("should dispatch collapseSections=undefined when feature_room_list_sections is disabled", () => {
viewModel = new RoomListViewModel({ client: matrixClient });
const dispatchSpy = jest.spyOn(dispatcher, "dispatch");
RoomListStoreV3.instance.emit(RoomListStoreV3Event.ListsUpdate);
expect(dispatchSpy).toHaveBeenCalledWith({
action: Action.RoomListSectionsCollapseStateChanged,
collapseSections: undefined,
});
});
});
describe("Keyboard navigation (ViewRoomDelta)", () => {
beforeEach(() => {
// stubClient sets up MatrixClientPeg which is needed when ViewRoom action is dispatched
@@ -724,7 +710,7 @@ describe("RoomListViewModel", () => {
});
});
describe("Sections (feature_room_list_sections)", () => {
describe("Sections", () => {
let favRoom1: Room;
let favRoom2: Room;
let lowPriorityRoom: Room;
@@ -732,11 +718,6 @@ describe("RoomListViewModel", () => {
let regularRoom2: Room;
beforeEach(() => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting: string) => {
if (setting === "feature_room_list_sections") return true;
return false;
});
favRoom1 = mkStubRoom("!fav1:server", "Fav 1", matrixClient);
favRoom2 = mkStubRoom("!fav2:server", "Fav 2", matrixClient);
lowPriorityRoom = mkStubRoom("!low1:server", "Low 1", matrixClient);
@@ -1030,7 +1011,6 @@ describe("RoomListViewModel", () => {
mkStubRoom("!space:server", "My Space", matrixClient),
]);
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting: string) => {
if (setting === "feature_room_list_sections") return true;
if (setting === "RoomList.CustomSectionData")
return {
[customTag]: { tag: customTag, name: "My Section", spaceId: "!space:server" },
@@ -1041,7 +1021,6 @@ describe("RoomListViewModel", () => {
it("shows an empty custom section when viewing its originating space", () => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting: string) => {
if (setting === "feature_room_list_sections") return true;
if (setting === "RoomList.CustomSectionData")
return { [customTag]: { tag: customTag, name: "My Section", spaceId: MetaSpace.Home } };
return false;