Files
Will Hunt a2f5c49438 Update settings toggles to use consistent design across app. (#30169)
* Use EditInPlace for identity server picker.

* Update test

* Add a test for setting an ID server.

* fix tests

* Reformat other :not sections

* forgot a comma

* Update Apperance settings to use toggle switches.

* Remove unused checkbox setting.

* Remove unused import.

* Update tests

* lint

* update apperance screenshot

* Begin replacing settings

* Refactor RoomPublishSetting

* Remove LabelledToggleSwitch

* Refactor SettingsFlag to use SettingsToggleInput

* Refactor CreateRoomDialog to use SettingsToggleInput

* Refactor DeclineAndBlockInviteDialog to use SettingsToggleInput

* Update DevtoolsDialog

* Refactor ReportRoomDialog to use SettingsToggle

* Update RoomUpgradeWarningDialog to use SettingsToggleInput

* Update WidgetCapabilitiesPromptDialog to use SettingsToggleInput

* Update trivial switchovers

* Update Notifications settings to use SettingsFlag where possible

* Update RoomPublishSetting and SpaceSettingVisibilityTab to use SettingsToggleInput with a warning

* revert changes to field

* Updated screenshots

* Prevent accidental submits

* Replace test ID tests

* Create new snapshot tests

* Add screenshot test for DeclineAndBlockDialog

* Add screenshot for create room dialog.

* Add devtools test

* Add upgrade rooms test

* Add widget capabilites prompt test

* Fix spec

* Add a test for the live location sharing prompt.

* fix copyright

* Add tests for notification settings

* Add tests for user security tab.

* Add test for room security tab.

* Add test for video settings tab.

* remove .only

* Test creating a video room

* Mask the IM name in the header.

* Add spaces vis tab test.

* Fixup unit tests to check correct attributes.

* Various fixes to components for tests.

* lint

* Update compound

* update setting names

* Cleanup tests

prettier

Updates some more playwright tests

Update more snapshots

Update switch

more fixes

drop .only

last screenshot round

fix video room flake

Remove console.logs

Remove roomId from devtools view.

lint

final screenshot

* Add playwright tests

* import pages/ remove duplicate create-room

* Update screenshots

* Fix accessibility for devtools

* Disable region test

* Fixup headers

* remove extra test

* Fix permissions dialog

* fixup tests

* update snapshot

* Update jest tests

* Clear up playwright tests

* update widget screenshot

* Fix wrong snaps from using wrong compound version

* Revert mistaken s/checkbox/switch/

* lint lint

* Update headings

* fix snap

* remove unused

* update snapshot

* update tab screenshot

* Update snapshots

* Fix margins

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Delint

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-12-04 15:46:21 +00:00

480 lines
22 KiB
TypeScript

/*
* 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 Page } from "@playwright/test";
import { expect, test } from "../../../element-web-test";
test.describe("Room list", () => {
test.use({
displayName: "Alice",
labsFlags: ["feature_new_room_list"],
botCreateOpts: {
displayName: "BotBob",
},
});
/**
* Get the room list
* @param page
*/
function getRoomList(page: Page) {
return page.getByTestId("room-list");
}
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("Room list", () => {
test.beforeEach(async ({ page, app, user }) => {
for (let i = 0; i < 30; i++) {
await app.client.createRoom({ name: `room${i}` });
}
});
test("should render the room list", { tag: "@screenshot" }, async ({ page, app, user, axe }) => {
const roomListView = getRoomList(page);
await expect(roomListView.getByRole("option", { name: "Open room room29" })).toBeVisible();
await expect(roomListView).toMatchScreenshot("room-list.png");
// Put focus on the room list
await roomListView.getByRole("option", { name: "Open room room29" }).click();
// Scroll to the end of the room list
await app.scrollListToBottom(roomListView);
// scrollListToBottom seems to leave the mouse hovered over the list, move it away.
await page.getByRole("button", { name: "User menu" }).hover();
await expect(axe).toHaveNoViolations();
await expect(roomListView).toMatchScreenshot("room-list-scrolled.png");
});
test("should open the room when it is clicked", async ({ page, app, user }) => {
const roomListView = getRoomList(page);
await roomListView.getByRole("option", { name: "Open room room29" }).click();
await expect(page.getByRole("heading", { name: "room29", level: 1 })).toBeVisible();
});
test("should open the context menu", { tag: "@screenshot" }, async ({ page, app, user }) => {
const roomListView = getRoomList(page);
await roomListView.getByRole("option", { name: "Open room room29" }).click({ button: "right" });
await expect(page.getByRole("menu", { name: "More Options" })).toBeVisible();
});
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" });
await roomItem.hover();
await expect(roomItem).toMatchScreenshot("room-list-item-hover.png");
const roomItemMenu = roomItem.getByRole("button", { name: "More Options" });
await roomItemMenu.click();
await expect(page).toMatchScreenshot("room-list-item-open-more-options.png");
// It should make the room favourited
await page.getByRole("menuitemcheckbox", { name: "Favourited" }).click();
// Check that the room is favourited
await roomItem.hover();
await roomItemMenu.click();
await expect(page.getByRole("menuitemcheckbox", { name: "Favourited" })).toBeChecked();
// It should show the invite dialog
await page.getByRole("menuitem", { name: "invite" }).click();
await expect(page.getByRole("heading", { name: "Invite to room29" })).toBeVisible();
await app.closeDialog();
// It should leave the room
await roomItem.hover();
await roomItemMenu.click();
await page.getByRole("menuitem", { name: "leave room" }).click();
await expect(roomItem).not.toBeVisible();
});
test("should open the notification options menu", { tag: "@screenshot" }, async ({ page, app, user }) => {
const roomListView = getRoomList(page);
const roomItem = roomListView.getByRole("option", { name: "Open room room29" });
await roomItem.hover();
await expect(roomItem).toMatchScreenshot("room-list-item-hover.png");
let roomItemMenu = roomItem.getByRole("button", { name: "Notification options" });
await roomItemMenu.click();
// Default settings should be selected
await expect(page.getByRole("menuitem", { name: "Match default settings" })).toHaveAttribute(
"aria-selected",
"true",
);
await expect(page).toMatchScreenshot("room-list-item-open-notification-options.png");
// It should make the room muted
await page.getByRole("menuitem", { name: "Mute room" }).click();
await expect(roomItem.getByTestId("notification-decoration")).not.toBeVisible();
// Put focus on the room list
await roomListView.getByRole("option", { name: "Open room room28" }).click();
// Scroll to the end of the room list
await app.scrollListToBottom(roomListView);
// The room decoration should have the muted icon
await expect(roomItem.getByTestId("notification-decoration")).toBeVisible();
await roomItem.hover();
// On hover, the room should show the muted icon
await expect(roomItem).toMatchScreenshot("room-list-item-hover-silent.png");
roomItemMenu = roomItem.getByRole("button", { name: "Notification options" });
await roomItemMenu.click();
// The Mute room option should be selected
await expect(page.getByRole("menuitem", { name: "Mute room" })).toHaveAttribute("aria-selected", "true");
await expect(page).toMatchScreenshot("room-list-item-open-notification-options-selection.png");
});
test("should scroll to the current room", async ({ page, app, user }) => {
const roomListView = getRoomList(page);
// Put focus on the room list
await roomListView.getByRole("option", { name: "Open room room29" }).click();
// Scroll to the end of the room list
await app.scrollListToBottom(roomListView);
await expect(roomListView.getByRole("option", { name: "Open room room0" })).toBeVisible();
await roomListView.getByRole("option", { name: "Open room room0" }).click();
const filters = page.getByRole("listbox", { name: "Room list filters" });
await filters.getByRole("option", { name: "People" }).click();
await expect(roomListView.getByRole("option", { name: "Open room room0" })).not.toBeVisible();
await filters.getByRole("option", { name: "People" }).click();
await expect(roomListView.getByRole("option", { name: "Open room room0" })).toBeVisible();
});
test.describe("Shortcuts", () => {
test("should select the next room", async ({ page, app, user }) => {
const roomListView = getRoomList(page);
await roomListView.getByRole("option", { name: "Open room room29" }).click();
await page.keyboard.press("Alt+ArrowDown");
await expect(page.getByRole("heading", { name: "room28", level: 1 })).toBeVisible();
});
test("should select the previous room", async ({ page, app, user }) => {
const roomListView = getRoomList(page);
await roomListView.getByRole("option", { name: "Open room room28" }).click();
await page.keyboard.press("Alt+ArrowUp");
await expect(page.getByRole("heading", { name: "room29", level: 1 })).toBeVisible();
});
test("should select the last room", async ({ page, app, user }) => {
const roomListView = getRoomList(page);
await roomListView.getByRole("option", { name: "Open room room29" }).click();
await page.keyboard.press("Alt+ArrowUp");
await expect(page.getByRole("heading", { name: "room0", level: 1 })).toBeVisible();
});
test("should select the next unread room", async ({ page, app, user, bot }) => {
const roomListView = getRoomList(page);
const roomId = await app.client.createRoom({ name: "1 notification" });
await app.client.inviteUser(roomId, bot.credentials.userId);
await bot.joinRoom(roomId);
await bot.sendMessage(roomId, "I am a robot. Beep.");
await roomListView.getByRole("option", { name: "Open room room20" }).click();
// Make sure the room with the unread is visible before we press the keyboard action to select it
await expect(roomListView.getByRole("option", { name: "1 notification" })).toBeVisible();
await page.keyboard.press("Alt+Shift+ArrowDown");
await expect(page.getByRole("heading", { name: "1 notification", level: 1 })).toBeVisible();
});
});
test.describe("Keyboard navigation", () => {
test("should navigate to the room list", async ({ page, app, user }) => {
const roomListView = getRoomList(page);
const room29 = roomListView.getByRole("option", { name: "Open room room29" });
const room28 = roomListView.getByRole("option", { name: "Open room room28" });
// open the room
await room29.click();
// put focus back on the room list item
await room29.click();
await expect(room29).toBeFocused();
await page.keyboard.press("ArrowDown");
await expect(room28).toBeFocused();
await expect(room29).not.toBeFocused();
await page.keyboard.press("ArrowUp");
await expect(room29).toBeFocused();
await expect(room28).not.toBeFocused();
});
test("should navigate to the notification menu", async ({ page, app, user }) => {
const roomListView = getRoomList(page);
const room29 = roomListView.getByRole("option", { name: "Open room room29" });
const moreButton = room29.getByRole("button", { name: "More options" });
const notificationButton = room29.getByRole("button", { name: "Notification options" });
await room29.click();
// put focus back on the room list item
await room29.click();
await page.keyboard.press("Tab");
await expect(moreButton).toBeFocused();
await page.keyboard.press("Tab");
await expect(notificationButton).toBeFocused();
// Open the menu
await page.keyboard.press("Enter");
// Wait for the menu to be open
await expect(page.getByRole("menuitem", { name: "Match default settings" })).toHaveAttribute(
"aria-selected",
"true",
);
await page.keyboard.press("ArrowDown");
await page.keyboard.press("Escape");
// Focus should be back on the notification button
await expect(notificationButton).toBeFocused();
});
test("should navigate to the top and then bottom of the room list", async ({ page, app, user }) => {
const roomListView = getRoomList(page);
const topRoom = roomListView.getByRole("option", { name: "Open room room29" });
// open the room
await topRoom.click();
// put focus back on the room list item
await topRoom.click();
await expect(topRoom).toBeFocused();
await page.keyboard.press("End");
const bottomRoom = roomListView.getByRole("option", { name: "Open room room0" });
await expect(bottomRoom).toBeFocused();
await page.keyboard.press("Home");
const topRoomAgain = roomListView.getByRole("option", { name: "Open room room29" });
await expect(topRoomAgain).toBeFocused();
});
});
});
test.describe("Avatar decoration", () => {
test.use({ labsFlags: ["feature_video_rooms", "feature_new_room_list"] });
test("should be a public room", { tag: "@screenshot" }, async ({ page, app, user }) => {
// @ts-ignore Visibility enum is not accessible
await app.client.createRoom({ name: "public room", visibility: "public" });
// focus the user menu to avoid to have hover decoration
await page.getByRole("button", { name: "User menu" }).focus();
const roomListView = getRoomList(page);
const publicRoom = roomListView.getByRole("option", { name: "public room" });
await expect(publicRoom).toBeVisible();
await expect(publicRoom).toMatchScreenshot("room-list-item-public.png");
});
test("should be a low priority room", { tag: "@screenshot" }, async ({ page, app, user }) => {
// @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" });
// Make room low priority
await publicRoom.click({ button: "right" });
await page.getByRole("menuitemcheckbox", { name: "Low priority" }).click();
// Should have low priority decoration
await expect(publicRoom.locator(".mx_RoomAvatarView_icon")).toHaveAccessibleName(
"This is a low priority room",
);
// focus the header to avoid to have hover decoration
await page.getByTestId("room-list-header").click();
await expect(publicRoom).toMatchScreenshot("room-list-item-low-priority.png");
});
test("should be a video room", { tag: "@screenshot" }, async ({ page, app, user }) => {
await page
.getByRole("navigation", { name: "Room list" })
.getByRole("button", { name: "New conversation" })
.click();
await page.getByRole("menuitem", { name: "New video room" }).click();
await page.getByRole("textbox", { name: "Name" }).fill("video room");
await page.getByRole("button", { name: "Create video room" }).click();
const roomListView = getRoomList(page);
const videoRoom = roomListView.getByRole("option", { name: "video room" });
// focus the user menu to avoid to have hover decoration
await page.getByRole("button", { name: "User menu" }).focus();
await expect(videoRoom).toBeVisible();
await expect(videoRoom).toMatchScreenshot("room-list-item-video.png");
});
});
test.describe("Notification decoration", () => {
test("should render the invitation decoration", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
const roomListView = getRoomList(page);
await bot.createRoom({
name: "invited room",
invite: [user.userId],
is_direct: true,
});
const invitedRoom = roomListView.getByRole("option", { name: "invited room" });
await expect(invitedRoom).toBeVisible();
await expect(invitedRoom).toMatchScreenshot("room-list-item-invited.png");
});
test("should render the regular decoration", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
const roomListView = getRoomList(page);
const roomId = await app.client.createRoom({ name: "2 notifications" });
await app.client.inviteUser(roomId, bot.credentials.userId);
await bot.joinRoom(roomId);
await bot.sendMessage(roomId, "I am a robot. Beep.");
await bot.sendMessage(roomId, "I am a robot. Beep.");
const room = roomListView.getByRole("option", { name: "2 notifications" });
await expect(room).toBeVisible();
await expect(room.getByTestId("notification-decoration")).toHaveText("2");
await expect(room).toMatchScreenshot("room-list-item-notification.png");
});
test("should render the mention decoration", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
const roomListView = getRoomList(page);
const roomId = await app.client.createRoom({ name: "mention" });
await app.client.inviteUser(roomId, bot.credentials.userId);
await bot.joinRoom(roomId);
const clientBot = await bot.prepareClient();
await clientBot.evaluate(
async (client, { roomId, userId }) => {
await client.sendMessage(roomId, {
// @ts-ignore ignore usage of MsgType.text
"msgtype": "m.text",
"body": "User",
"format": "org.matrix.custom.html",
"formatted_body": `<a href="https://matrix.to/#/${userId}">User</a>`,
"m.mentions": {
user_ids: [userId],
},
});
},
{ roomId, userId: user.userId },
);
await bot.sendMessage(roomId, "I am a robot. Beep.");
const room = roomListView.getByRole("option", { name: "mention" });
await expect(room).toBeVisible();
await expect(room).toMatchScreenshot("room-list-item-mention.png");
});
test("should render a message preview", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
await app.settings.openUserSettings("Preferences");
await page.getByRole("switch", { name: "Show message previews" }).click();
await app.closeDialog();
const roomListView = getRoomList(page);
const roomId = await app.client.createRoom({ name: "activity" });
// focus the user menu to avoid to have hover decoration
await page.getByRole("button", { name: "User menu" }).focus();
await app.client.inviteUser(roomId, bot.credentials.userId);
await bot.joinRoom(roomId);
await bot.sendMessage(roomId, "I am a robot. Beep.");
const room = roomListView.getByRole("option", { name: "activity" });
await expect(room.getByText("I am a robot. Beep.")).toBeVisible();
await expect(room).toMatchScreenshot("room-list-item-message-preview.png");
});
test("should render an activity decoration", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
const roomListView = getRoomList(page);
const otherRoomId = await app.client.createRoom({ name: "other room" });
const roomId = await app.client.createRoom({ name: "activity" });
await app.client.inviteUser(roomId, bot.credentials.userId);
await bot.joinRoom(roomId);
await app.viewRoomById(roomId);
await app.settings.openRoomSettings("Notifications");
await page.getByText("@mentions & keywords").click();
await app.settings.closeDialog();
await app.settings.openUserSettings("Notifications");
await page
.getByRole("switch", { name: "Show all activity in the room list (dots or number of unread messages)" })
.check();
await app.settings.closeDialog();
// Switch to the other room to avoid the notification to be cleared
await app.viewRoomById(otherRoomId);
await bot.sendMessage(roomId, "I am a robot. Beep.");
const room = roomListView.getByRole("option", { name: "activity" });
await expect(room.getByTestId("notification-decoration")).toBeVisible();
await expect(room).toMatchScreenshot("room-list-item-activity.png");
});
test("should render a mark as unread decoration", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
const roomListView = getRoomList(page);
const roomId = await app.client.createRoom({ name: "mark as unread" });
await app.client.inviteUser(roomId, bot.credentials.userId);
await bot.joinRoom(roomId);
const room = roomListView.getByRole("option", { name: "mark as unread" });
await room.click({ button: "right" });
await page.getByRole("menuitem", { name: "mark as unread" }).click();
// focus the header to avoid to have hover decoration
await page.getByTestId("room-list-header").click();
await expect(room).toMatchScreenshot("room-list-item-mark-as-unread.png");
});
test("should render silent decoration", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
const roomListView = getRoomList(page);
const roomId = await app.client.createRoom({ name: "silent" });
await app.client.inviteUser(roomId, bot.credentials.userId);
await bot.joinRoom(roomId);
await app.viewRoomById(roomId);
await app.settings.openRoomSettings("Notifications");
await page.getByText("Off").click();
await app.settings.closeDialog();
const room = roomListView.getByRole("option", { name: "silent" });
await expect(room.getByTestId("notification-decoration")).toBeVisible();
await expect(room).toMatchScreenshot("room-list-item-silent.png");
});
});
});