Add Playwright tests for settings toggles (#30318)
* 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/
@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2024 New Vector Ltd.
|
|
||||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
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 { test, expect } from "../../element-web-test";
|
|
||||||
|
|
||||||
test.describe("Create Room", () => {
|
|
||||||
test.use({ displayName: "Jim" });
|
|
||||||
|
|
||||||
test("should allow us to create a public room with name, topic & address set", async ({ page, user, app }) => {
|
|
||||||
const name = "Test room 1";
|
|
||||||
const topic = "This room is dedicated to this test and this test only!";
|
|
||||||
|
|
||||||
const dialog = await app.openCreateRoomDialog();
|
|
||||||
// Fill name & topic
|
|
||||||
await dialog.getByRole("textbox", { name: "Name" }).fill(name);
|
|
||||||
await dialog.getByRole("textbox", { name: "Topic" }).fill(topic);
|
|
||||||
// Change room to public
|
|
||||||
await dialog.getByRole("button", { name: "Room visibility" }).click();
|
|
||||||
await dialog.getByRole("option", { name: "Public room" }).click();
|
|
||||||
// Fill room address
|
|
||||||
await dialog.getByRole("textbox", { name: "Room address" }).fill("test-room-1");
|
|
||||||
// Submit
|
|
||||||
await dialog.getByRole("button", { name: "Create room" }).click();
|
|
||||||
|
|
||||||
await expect(page).toHaveURL(new RegExp(`/#/room/#test-room-1:${user.homeServer}`));
|
|
||||||
const header = page.locator(".mx_RoomHeader");
|
|
||||||
await expect(header).toContainText(name);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
33
playwright/e2e/devtools/devtools.spec.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
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 { test, expect } from "../../element-web-test";
|
||||||
|
|
||||||
|
test.describe("Devtools", () => {
|
||||||
|
test.use({
|
||||||
|
displayName: "Alice",
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should render the devtools", { tag: "@screenshot" }, async ({ page, homeserver, user, app, axe }) => {
|
||||||
|
await app.client.createRoom({ name: "Test Room" });
|
||||||
|
await app.viewRoomByName("Test Room");
|
||||||
|
|
||||||
|
const composer = app.getComposer().locator("[contenteditable]");
|
||||||
|
await composer.fill("/devtools");
|
||||||
|
await composer.press("Enter");
|
||||||
|
const dialog = page.locator(".mx_Dialog");
|
||||||
|
await dialog.getByLabel("Developer mode").check();
|
||||||
|
|
||||||
|
axe.disableRules("color-contrast"); // XXX: Inheriting colour contrast issues from room view.
|
||||||
|
await expect(axe).toHaveNoViolations();
|
||||||
|
await expect(dialog).toMatchScreenshot("devtools-dialog.png", {
|
||||||
|
css: `.mx_CopyableText {
|
||||||
|
display: none;
|
||||||
|
}`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
38
playwright/e2e/devtools/upgraderoom.spec.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
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 { SettingLevel } from "../../../src/settings/SettingLevel";
|
||||||
|
import { test, expect } from "../../element-web-test";
|
||||||
|
|
||||||
|
test.describe("Room upgrade dialog", () => {
|
||||||
|
test.use({
|
||||||
|
displayName: "Alice",
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
"should render the room upgrade dialog",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async ({ page, homeserver, user, app, axe }) => {
|
||||||
|
// Enable developer mode
|
||||||
|
await app.settings.setValue("developerMode", null, SettingLevel.ACCOUNT, true);
|
||||||
|
|
||||||
|
await app.client.createRoom({ name: "Test Room" });
|
||||||
|
await app.viewRoomByName("Test Room");
|
||||||
|
|
||||||
|
const composer = app.getComposer().locator("[contenteditable]");
|
||||||
|
// Pick a room version that is likely to be supported by all our target homeservers.
|
||||||
|
await composer.fill("/upgraderoom 5");
|
||||||
|
await composer.press("Enter");
|
||||||
|
const dialog = page.locator(".mx_Dialog");
|
||||||
|
await dialog.getByLabel("Automatically invite members from this room to the new one").check();
|
||||||
|
|
||||||
|
axe.disableRules("color-contrast"); // XXX: Inheriting colour contrast issues from room view.
|
||||||
|
await expect(axe).toHaveNoViolations();
|
||||||
|
await expect(dialog).toMatchScreenshot("upgrade-room.png");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
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 { test, expect } from "../../element-web-test";
|
||||||
|
|
||||||
|
test.describe("Decline and block invite dialog", function () {
|
||||||
|
test.use({
|
||||||
|
displayName: "Hanako",
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
"should show decline and block dialog for a room",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async ({ page, app, user, bot, axe }) => {
|
||||||
|
await bot.createRoom({ name: "Test Room", invite: [user.userId] });
|
||||||
|
await app.viewRoomByName("Test Room");
|
||||||
|
await page.getByRole("button", { name: "Decline and block" }).click();
|
||||||
|
|
||||||
|
axe.disableRules("color-contrast"); // XXX: Inheriting colour contrast issues from room view.
|
||||||
|
await expect(axe).toHaveNoViolations();
|
||||||
|
await expect(page.locator(".mx_Dialog")).toMatchScreenshot("decline-and-block-invite-empty.png");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2024 New Vector Ltd.
|
Copyright 2024, 2025 New Vector Ltd.
|
||||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
@ -57,4 +57,26 @@ test.describe("Location sharing", { tag: "@no-firefox" }, () => {
|
|||||||
|
|
||||||
await expect(page.locator(".mx_Marker")).toBeVisible();
|
await expect(page.locator(".mx_Marker")).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
"is prompted for and can consent to live location sharing",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async ({ page, user, app, axe }) => {
|
||||||
|
await app.viewRoomById(await app.client.createRoom({}));
|
||||||
|
|
||||||
|
const composerOptions = await app.openMessageComposerOptions();
|
||||||
|
await composerOptions.getByRole("menuitem", { name: "Location", exact: true }).click();
|
||||||
|
const menu = page.locator(".mx_LocationShareMenu");
|
||||||
|
|
||||||
|
await menu.getByRole("button", { name: "My live location" }).click();
|
||||||
|
await menu.getByLabel("Enable live location sharing").check();
|
||||||
|
|
||||||
|
axe.disableRules([
|
||||||
|
"color-contrast", // XXX: Inheriting colour contrast issues from room view.
|
||||||
|
"region", // XXX: ContextMenu managed=false does not provide a role.
|
||||||
|
]);
|
||||||
|
await expect(axe).toHaveNoViolations();
|
||||||
|
await expect(menu).toMatchScreenshot("location-live-share-dialog.png");
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
68
playwright/e2e/room/create-room.spec.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 New Vector Ltd.
|
||||||
|
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
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 { SettingLevel } from "../../../src/settings/SettingLevel";
|
||||||
|
import { test, expect } from "../../element-web-test";
|
||||||
|
|
||||||
|
const name = "Test room";
|
||||||
|
const topic = "A decently explanatory topic for a test room.";
|
||||||
|
|
||||||
|
test.describe("Create Room", () => {
|
||||||
|
test.use({ displayName: "Jim" });
|
||||||
|
|
||||||
|
test(
|
||||||
|
"should create a public room with name, topic & address set",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async ({ page, user, app, axe }) => {
|
||||||
|
const dialog = await app.openCreateRoomDialog();
|
||||||
|
// Fill name & topic
|
||||||
|
await dialog.getByRole("textbox", { name: "Name" }).fill(name);
|
||||||
|
await dialog.getByRole("textbox", { name: "Topic" }).fill(topic);
|
||||||
|
// Change room to public
|
||||||
|
await dialog.getByRole("button", { name: "Room visibility" }).click();
|
||||||
|
await dialog.getByRole("option", { name: "Public room" }).click();
|
||||||
|
// Fill room address
|
||||||
|
await dialog.getByRole("textbox", { name: "Room address" }).fill("test-create-room-standard");
|
||||||
|
|
||||||
|
axe.disableRules("color-contrast"); // XXX: Inheriting colour contrast issues from room view.
|
||||||
|
await expect(axe).toHaveNoViolations();
|
||||||
|
// Snapshot it
|
||||||
|
await expect(dialog).toMatchScreenshot("create-room.png");
|
||||||
|
|
||||||
|
// Submit
|
||||||
|
await dialog.getByRole("button", { name: "Create room" }).click();
|
||||||
|
|
||||||
|
await expect(page).toHaveURL(new RegExp(`/#/room/#test-create-room-standard:${user.homeServer}`));
|
||||||
|
const header = page.locator(".mx_RoomHeader");
|
||||||
|
await expect(header).toContainText(name);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test("should create a video room", { tag: "@screenshot" }, async ({ page, user, app }) => {
|
||||||
|
await app.settings.setValue("feature_video_rooms", null, SettingLevel.DEVICE, true);
|
||||||
|
|
||||||
|
const dialog = await app.openCreateRoomDialog("New video room");
|
||||||
|
// Fill name & topic
|
||||||
|
await dialog.getByRole("textbox", { name: "Name" }).fill(name);
|
||||||
|
await dialog.getByRole("textbox", { name: "Topic" }).fill(topic);
|
||||||
|
// Change room to public
|
||||||
|
await dialog.getByRole("button", { name: "Room visibility" }).click();
|
||||||
|
await dialog.getByRole("option", { name: "Public room" }).click();
|
||||||
|
// Fill room address
|
||||||
|
await dialog.getByRole("textbox", { name: "Room address" }).fill("test-create-room-video");
|
||||||
|
// Snapshot it
|
||||||
|
await expect(dialog).toMatchScreenshot("create-video-room.png");
|
||||||
|
|
||||||
|
// Submit
|
||||||
|
await dialog.getByRole("button", { name: "Create video room" }).click();
|
||||||
|
|
||||||
|
await expect(page).toHaveURL(new RegExp(`/#/room/#test-create-room-video:${user.homeServer}`));
|
||||||
|
const header = page.locator(".mx_RoomHeader");
|
||||||
|
await expect(header).toContainText(name);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
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 { test, expect } from "../../../element-web-test";
|
||||||
|
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||||
|
|
||||||
|
test.describe("Notifications 2 tab", () => {
|
||||||
|
test.use({
|
||||||
|
displayName: "Alice",
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should display notification settings", { tag: "@screenshot" }, async ({ page, app, user, axe }) => {
|
||||||
|
await app.settings.setValue("feature_notification_settings2", null, SettingLevel.DEVICE, true);
|
||||||
|
await page.setViewportSize({ width: 1024, height: 2000 });
|
||||||
|
const settings = await app.settings.openUserSettings("Notifications");
|
||||||
|
|
||||||
|
axe.disableRules("color-contrast"); // XXX: Inheriting colour contrast issues from room view.
|
||||||
|
await expect(axe).toHaveNoViolations();
|
||||||
|
await expect(settings).toMatchScreenshot("standard-notifications-2-settings.png", {
|
||||||
|
// Mask the mxid.
|
||||||
|
mask: [settings.locator("#mx_NotificationSettings2_MentionCheckbox span")],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
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 { test, expect } from "../../../element-web-test";
|
||||||
|
|
||||||
|
test.describe("Notifications tab", () => {
|
||||||
|
test.use({
|
||||||
|
displayName: "Alice",
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should display notification settings", { tag: "@screenshot" }, async ({ page, app, user, axe }) => {
|
||||||
|
await page.setViewportSize({ width: 1024, height: 1400 });
|
||||||
|
const settings = await app.settings.openUserSettings("Notifications");
|
||||||
|
await settings.getByLabel("Enable notifications for this account").check();
|
||||||
|
await settings.getByLabel("Enable notifications for this device").check();
|
||||||
|
|
||||||
|
axe.disableRules("color-contrast"); // XXX: Inheriting colour contrast issues from room view.
|
||||||
|
await expect(axe).toHaveNoViolations();
|
||||||
|
await expect(settings).toMatchScreenshot("standard-notification-settings.png");
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import { type Locator } from "@playwright/test";
|
import { type Locator } from "@playwright/test";
|
||||||
|
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../../element-web-test";
|
||||||
|
|
||||||
test.describe("Roles & Permissions room settings tab", () => {
|
test.describe("Roles & Permissions room settings tab", () => {
|
||||||
const roomName = "Test room";
|
const roomName = "Test room";
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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 } from "@playwright/test";
|
||||||
|
|
||||||
|
import { test, expect } from "../../../element-web-test";
|
||||||
|
|
||||||
|
test.describe("Roles & Permissions room settings tab", () => {
|
||||||
|
const roomName = "Test room";
|
||||||
|
|
||||||
|
test.use({
|
||||||
|
displayName: "Alice",
|
||||||
|
});
|
||||||
|
|
||||||
|
let settings: Locator;
|
||||||
|
|
||||||
|
test.beforeEach(async ({ user, app }) => {
|
||||||
|
await app.client.createRoom({ name: roomName });
|
||||||
|
await app.viewRoomByName(roomName);
|
||||||
|
settings = await app.settings.openRoomSettings("Security & Privacy");
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
"should be able to toggle on encryption in a room",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async ({ page, app, user, axe }) => {
|
||||||
|
await page.setViewportSize({ width: 1024, height: 1400 });
|
||||||
|
const encryptedToggle = settings.getByLabel("Encrypted");
|
||||||
|
await encryptedToggle.click();
|
||||||
|
|
||||||
|
// Accept the dialog.
|
||||||
|
await page.getByRole("button", { name: "Ok " }).click();
|
||||||
|
|
||||||
|
await expect(encryptedToggle).toBeChecked();
|
||||||
|
await expect(encryptedToggle).toBeDisabled();
|
||||||
|
|
||||||
|
await settings.getByLabel("Only send messages to verified users.").check();
|
||||||
|
|
||||||
|
axe.disableRules("color-contrast"); // XXX: Inheriting colour contrast issues from room view.
|
||||||
|
await expect(axe).toHaveNoViolations();
|
||||||
|
await expect(settings).toMatchScreenshot("room-security-settings.png");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
42
playwright/e2e/settings/room-settings/room-video-tab.spec.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* 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 } from "@playwright/test";
|
||||||
|
|
||||||
|
import { test, expect } from "../../../element-web-test";
|
||||||
|
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||||
|
|
||||||
|
test.describe("Voice & Video room settings tab", () => {
|
||||||
|
const roomName = "Test room";
|
||||||
|
|
||||||
|
test.use({
|
||||||
|
displayName: "Alice",
|
||||||
|
});
|
||||||
|
|
||||||
|
let settings: Locator;
|
||||||
|
|
||||||
|
test.beforeEach(async ({ user, app, page }) => {
|
||||||
|
// Execute client actions before setting, as the setting will force a reload.
|
||||||
|
await app.client.createRoom({ name: roomName });
|
||||||
|
await app.settings.setValue("feature_group_calls", null, SettingLevel.DEVICE, true);
|
||||||
|
await app.viewRoomByName(roomName);
|
||||||
|
settings = await app.settings.openRoomSettings("Voice & Video");
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
"should be able to toggle on Element Call in the room",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async ({ page, app, user, axe }) => {
|
||||||
|
await page.setViewportSize({ width: 1024, height: 1400 });
|
||||||
|
const callToggle = settings.getByLabel("Enable Element Call as an additional calling option in this room");
|
||||||
|
await callToggle.check();
|
||||||
|
axe.disableRules("color-contrast"); // XXX: Inheriting colour contrast issues from room view.
|
||||||
|
await expect(axe).toHaveNoViolations();
|
||||||
|
await expect(settings).toMatchScreenshot("room-video-settings.png");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
@ -41,6 +41,18 @@ test.describe("Security user settings tab", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should render the security tab", { tag: "@screenshot" }, async ({ app, page, user }) => {
|
||||||
|
await page.setViewportSize({ width: 1024, height: 1400 });
|
||||||
|
const tab = await app.settings.openUserSettings("Security");
|
||||||
|
await expect(tab).toMatchScreenshot("security-settings-tab.png", {
|
||||||
|
mask: [
|
||||||
|
// Contains IM name.
|
||||||
|
tab.locator("#mx_SetIntegrationManager_BodyText"),
|
||||||
|
tab.locator("#mx_SetIntegrationManager_ManagerName"),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test("should be able to set an ID server", async ({ app, context, user, page }) => {
|
test("should be able to set an ID server", async ({ app, context, user, page }) => {
|
||||||
const tab = await app.settings.openUserSettings("Security");
|
const tab = await app.settings.openUserSettings("Security");
|
||||||
|
|
||||||
|
|||||||
@ -376,4 +376,19 @@ test.describe("Spaces", () => {
|
|||||||
await app.viewSpaceByName("Root Space");
|
await app.viewSpaceByName("Root Space");
|
||||||
await expect(page.locator(".mx_SpaceRoomView")).toMatchScreenshot("space-room-view.png");
|
await expect(page.locator(".mx_SpaceRoomView")).toMatchScreenshot("space-room-view.png");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should render spaces visibility settings", { tag: "@screenshot" }, async ({ page, app, user, axe }) => {
|
||||||
|
await app.client.createSpace({
|
||||||
|
name: "My Space",
|
||||||
|
});
|
||||||
|
await app.viewSpaceByName("My space");
|
||||||
|
await page.getByLabel("Settings", { exact: true }).click();
|
||||||
|
await app.settings.switchTab("Visibility");
|
||||||
|
|
||||||
|
axe.disableRules("color-contrast"); // XXX: Inheriting colour contrast issues from room view.
|
||||||
|
await expect(axe).toHaveNoViolations();
|
||||||
|
await expect(page.locator("#mx_tabpanel_SPACE_VISIBILITY_TAB")).toMatchScreenshot(
|
||||||
|
"space-visibility-settings.png",
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
98
playwright/e2e/widgets/permissions-dialog.spec.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
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 { test, expect } from "../../element-web-test";
|
||||||
|
|
||||||
|
const DEMO_WIDGET_ID = "demo-widget-id";
|
||||||
|
const DEMO_WIDGET_NAME = "Demo Widget";
|
||||||
|
const DEMO_WIDGET_TYPE = "demo";
|
||||||
|
const ROOM_NAME = "Demo";
|
||||||
|
|
||||||
|
const DEMO_WIDGET_HTML = `
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Demo Widget</title>
|
||||||
|
<script>
|
||||||
|
let sendEventCount = 0
|
||||||
|
window.onmessage = ev => {
|
||||||
|
if (ev.data.action === 'capabilities') {
|
||||||
|
window.parent.postMessage(Object.assign({
|
||||||
|
response: {
|
||||||
|
capabilities: [
|
||||||
|
"org.matrix.msc2762.timeline:*",
|
||||||
|
"org.matrix.msc2762.receive.state_event:m.room.topic",
|
||||||
|
"org.matrix.msc2762.send.event:net.widget_echo"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}, ev.data), '*');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
test.describe("Widger permissions dialog", () => {
|
||||||
|
test.use({
|
||||||
|
displayName: "Mike",
|
||||||
|
});
|
||||||
|
|
||||||
|
let demoWidgetUrl: string;
|
||||||
|
test.beforeEach(async ({ webserver }) => {
|
||||||
|
demoWidgetUrl = webserver.start(DEMO_WIDGET_HTML);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
"should be updated if user is re-invited into the room with updated state event",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async ({ page, app, user, axe }) => {
|
||||||
|
const roomId = await app.client.createRoom({
|
||||||
|
name: ROOM_NAME,
|
||||||
|
});
|
||||||
|
|
||||||
|
// setup widget via state event
|
||||||
|
await app.client.sendStateEvent(
|
||||||
|
roomId,
|
||||||
|
"im.vector.modular.widgets",
|
||||||
|
{
|
||||||
|
id: DEMO_WIDGET_ID,
|
||||||
|
creatorUserId: "somebody",
|
||||||
|
type: DEMO_WIDGET_TYPE,
|
||||||
|
name: DEMO_WIDGET_NAME,
|
||||||
|
url: demoWidgetUrl,
|
||||||
|
},
|
||||||
|
DEMO_WIDGET_ID,
|
||||||
|
);
|
||||||
|
|
||||||
|
// set initial layout
|
||||||
|
await app.client.sendStateEvent(
|
||||||
|
roomId,
|
||||||
|
"io.element.widgets.layout",
|
||||||
|
{
|
||||||
|
widgets: {
|
||||||
|
[DEMO_WIDGET_ID]: {
|
||||||
|
container: "top",
|
||||||
|
index: 1,
|
||||||
|
width: 100,
|
||||||
|
height: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
|
// open the room
|
||||||
|
await app.viewRoomByName(ROOM_NAME);
|
||||||
|
|
||||||
|
axe.disableRules("color-contrast"); // XXX: Inheriting colour contrast issues from room view.
|
||||||
|
await expect(axe).toHaveNoViolations();
|
||||||
|
await expect(page.locator(".mx_WidgetCapabilitiesPromptDialog")).toMatchScreenshot(
|
||||||
|
"widget-capabilites-prompt.png",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
@ -51,9 +51,9 @@ export class ElementAppPage {
|
|||||||
/**
|
/**
|
||||||
* Open room creation dialog.
|
* Open room creation dialog.
|
||||||
*/
|
*/
|
||||||
public async openCreateRoomDialog(): Promise<Locator> {
|
public async openCreateRoomDialog(roomKindname: "New room" | "New video room" = "New room"): Promise<Locator> {
|
||||||
await this.page.getByRole("button", { name: "Add room", exact: true }).click();
|
await this.page.getByRole("button", { name: "Add room", exact: true }).click();
|
||||||
await this.page.getByRole("menuitem", { name: "New room", exact: true }).click();
|
await this.page.getByRole("menuitem", { name: roomKindname, exact: true }).click();
|
||||||
return this.page.locator(".mx_CreateRoomDialog");
|
return this.page.locator(".mx_CreateRoomDialog");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export class Settings {
|
|||||||
* @param {*} value The new value of the setting, may be null.
|
* @param {*} value The new value of the setting, may be null.
|
||||||
* @return {Promise} Resolves when the setting has been changed.
|
* @return {Promise} Resolves when the setting has been changed.
|
||||||
*/
|
*/
|
||||||
public async setValue(settingName: string, roomId: string, level: SettingLevel, value: any): Promise<void> {
|
public async setValue(settingName: string, roomId: string | null, level: SettingLevel, value: any): Promise<void> {
|
||||||
return this.page.evaluate<
|
return this.page.evaluate<
|
||||||
Promise<void>,
|
Promise<void>,
|
||||||
{
|
{
|
||||||
|
|||||||
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 185 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 31 KiB |
@ -24,6 +24,12 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_DevTools_toolHeading {
|
||||||
|
color: var(--cpd-color-text-secondary);
|
||||||
|
font-weight: var(--cpd-font-weight-semibold);
|
||||||
|
font-size: var(--cpd-font-size-heading-sm);
|
||||||
|
}
|
||||||
|
|
||||||
.mx_DevTools_content {
|
.mx_DevTools_content {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -208,7 +208,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h1 {
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,6 +37,8 @@ const WidgetAvatar: React.FC<IProps> = ({ app, className, size = "20px", ...prop
|
|||||||
return (
|
return (
|
||||||
<BaseAvatar
|
<BaseAvatar
|
||||||
{...props}
|
{...props}
|
||||||
|
// Span elements cannot have a label
|
||||||
|
role="img"
|
||||||
name={app.id}
|
name={app.id}
|
||||||
className={classNames("mx_WidgetAvatar", className)}
|
className={classNames("mx_WidgetAvatar", className)}
|
||||||
// MSC2765
|
// MSC2765
|
||||||
|
|||||||
@ -84,7 +84,9 @@ const DevtoolsDialog: React.FC<IProps> = ({ roomId, threadRootId, onFinished })
|
|||||||
<BaseTool onBack={onBack}>
|
<BaseTool onBack={onBack}>
|
||||||
{Object.entries(Tools).map(([category, tools]) => (
|
{Object.entries(Tools).map(([category, tools]) => (
|
||||||
<div key={category}>
|
<div key={category}>
|
||||||
<h3>{_t(categoryLabels[category as unknown as Category])}</h3>
|
<h2 className="mx_DevTools_toolHeading">
|
||||||
|
{_t(categoryLabels[category as unknown as Category])}
|
||||||
|
</h2>
|
||||||
{tools.map(([label, tool]) => {
|
{tools.map(([label, tool]) => {
|
||||||
const onClick = (): void => {
|
const onClick = (): void => {
|
||||||
setTool([label, tool]);
|
setTool([label, tool]);
|
||||||
@ -98,7 +100,7 @@ const DevtoolsDialog: React.FC<IProps> = ({ roomId, threadRootId, onFinished })
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<div>
|
<div>
|
||||||
<h3>{_t("common|options")}</h3>
|
<h2 className="mx_DevTools_toolHeading">{_t("common|options")}</h2>
|
||||||
<SettingsFlag name="developerMode" level={SettingLevel.ACCOUNT} />
|
<SettingsFlag name="developerMode" level={SettingLevel.ACCOUNT} />
|
||||||
<SettingsFlag name="showHiddenEventsInTimeline" level={SettingLevel.DEVICE} />
|
<SettingsFlag name="showHiddenEventsInTimeline" level={SettingLevel.DEVICE} />
|
||||||
<SettingsFlag name="enableWidgetScreenshots" level={SettingLevel.ACCOUNT} />
|
<SettingsFlag name="enableWidgetScreenshots" level={SettingLevel.ACCOUNT} />
|
||||||
|
|||||||
@ -572,7 +572,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
|||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<WidgetAvatar app={this.props.app} size="20px" />
|
<WidgetAvatar app={this.props.app} size="20px" />
|
||||||
<h3>{name}</h3>
|
<h1>{name}</h1>
|
||||||
<span>
|
<span>
|
||||||
{title ? titleSpacer : ""}
|
{title ? titleSpacer : ""}
|
||||||
{title}
|
{title}
|
||||||
|
|||||||
@ -64,7 +64,7 @@ const WidgetCard: React.FC<IProps> = ({ room, widgetId, onClose }) => {
|
|||||||
|
|
||||||
const header = (
|
const header = (
|
||||||
<div className="mx_BaseCard_header_title">
|
<div className="mx_BaseCard_header_title">
|
||||||
<Heading size="4" className="mx_BaseCard_header_title_heading">
|
<Heading size="4" className="mx_BaseCard_header_title_heading" as="h1">
|
||||||
{WidgetUtils.getWidgetName(app)}
|
{WidgetUtils.getWidgetName(app)}
|
||||||
</Heading>
|
</Heading>
|
||||||
<ContextMenuButton
|
<ContextMenuButton
|
||||||
|
|||||||
@ -807,7 +807,7 @@ export default class Notifications extends React.PureComponent<EmptyObject, ISta
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div data-testid={`notif-section-${category}`} className="mx_UserNotifSettings_grid">
|
<div data-testid={`notif-section-${category}`} className="mx_UserNotifSettings_grid">
|
||||||
<SettingsSubsectionHeading heading={sectionName} />
|
<SettingsSubsectionHeading heading={sectionName} as="h2" />
|
||||||
<span className="mx_UserNotifSettings_gridColumnLabel">{VectorStateToLabel[VectorState.Off]}</span>
|
<span className="mx_UserNotifSettings_gridColumnLabel">{VectorStateToLabel[VectorState.Off]}</span>
|
||||||
<span className="mx_UserNotifSettings_gridColumnLabel">{VectorStateToLabel[VectorState.On]}</span>
|
<span className="mx_UserNotifSettings_gridColumnLabel">{VectorStateToLabel[VectorState.On]}</span>
|
||||||
<span className="mx_UserNotifSettings_gridColumnLabel">{VectorStateToLabel[VectorState.Loud]}</span>
|
<span className="mx_UserNotifSettings_gridColumnLabel">{VectorStateToLabel[VectorState.Loud]}</span>
|
||||||
|
|||||||
@ -34,6 +34,7 @@ import { SettingsSection } from "../shared/SettingsSection";
|
|||||||
import { SettingsSubsection } from "../shared/SettingsSubsection";
|
import { SettingsSubsection } from "../shared/SettingsSubsection";
|
||||||
import { NotificationPusherSettings } from "./NotificationPusherSettings";
|
import { NotificationPusherSettings } from "./NotificationPusherSettings";
|
||||||
import SettingsFlag from "../../elements/SettingsFlag";
|
import SettingsFlag from "../../elements/SettingsFlag";
|
||||||
|
import { SettingsSubsectionHeading } from "../shared/SettingsSubsectionHeading";
|
||||||
|
|
||||||
enum NotificationDefaultLevels {
|
enum NotificationDefaultLevels {
|
||||||
AllMessages = "all_messages",
|
AllMessages = "all_messages",
|
||||||
@ -151,7 +152,12 @@ export default function NotificationSettings2(): JSX.Element {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<SettingsSubsection
|
<SettingsSubsection
|
||||||
|
heading={
|
||||||
|
<SettingsSubsectionHeading
|
||||||
heading={_t("settings|notifications|default_setting_section")}
|
heading={_t("settings|notifications|default_setting_section")}
|
||||||
|
as="h2"
|
||||||
|
/>
|
||||||
|
}
|
||||||
description={_t("settings|notifications|default_setting_description")}
|
description={_t("settings|notifications|default_setting_description")}
|
||||||
>
|
>
|
||||||
<StyledRadioGroup
|
<StyledRadioGroup
|
||||||
@ -178,7 +184,12 @@ export default function NotificationSettings2(): JSX.Element {
|
|||||||
/>
|
/>
|
||||||
</SettingsSubsection>
|
</SettingsSubsection>
|
||||||
<SettingsSubsection
|
<SettingsSubsection
|
||||||
|
heading={
|
||||||
|
<SettingsSubsectionHeading
|
||||||
heading={_t("settings|notifications|play_sound_for_section")}
|
heading={_t("settings|notifications|play_sound_for_section")}
|
||||||
|
as="h2"
|
||||||
|
/>
|
||||||
|
}
|
||||||
description={_t("settings|notifications|play_sound_for_description")}
|
description={_t("settings|notifications|play_sound_for_description")}
|
||||||
>
|
>
|
||||||
<LabelledCheckbox
|
<LabelledCheckbox
|
||||||
@ -224,7 +235,9 @@ export default function NotificationSettings2(): JSX.Element {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SettingsSubsection>
|
</SettingsSubsection>
|
||||||
<SettingsSubsection heading={_t("settings|notifications|other_section")}>
|
<SettingsSubsection
|
||||||
|
heading={<SettingsSubsectionHeading heading={_t("settings|notifications|other_section")} as="h2" />}
|
||||||
|
>
|
||||||
<LabelledCheckbox
|
<LabelledCheckbox
|
||||||
label={_t("settings|notifications|invites")}
|
label={_t("settings|notifications|invites")}
|
||||||
value={settings.activity.invite}
|
value={settings.activity.invite}
|
||||||
@ -269,7 +282,9 @@ export default function NotificationSettings2(): JSX.Element {
|
|||||||
/>
|
/>
|
||||||
</SettingsSubsection>
|
</SettingsSubsection>
|
||||||
<SettingsSubsection
|
<SettingsSubsection
|
||||||
heading={_t("settings|notifications|mentions_keywords")}
|
heading={
|
||||||
|
<SettingsSubsectionHeading heading={_t("settings|notifications|mentions_keywords")} as="h2" />
|
||||||
|
}
|
||||||
description={_t(
|
description={_t(
|
||||||
"settings|notifications|keywords",
|
"settings|notifications|keywords",
|
||||||
{},
|
{},
|
||||||
|
|||||||
@ -12,13 +12,19 @@ import Heading from "../../typography/Heading";
|
|||||||
|
|
||||||
export interface SettingsSubsectionHeadingProps extends HTMLAttributes<HTMLDivElement> {
|
export interface SettingsSubsectionHeadingProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
heading: string;
|
heading: string;
|
||||||
|
as?: React.ComponentProps<typeof Heading>["as"];
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SettingsSubsectionHeading: React.FC<SettingsSubsectionHeadingProps> = ({ heading, children, ...rest }) => {
|
export const SettingsSubsectionHeading: React.FC<SettingsSubsectionHeadingProps> = ({
|
||||||
|
heading,
|
||||||
|
as = "h3",
|
||||||
|
children,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div {...rest} className="mx_SettingsSubsectionHeading">
|
<div {...rest} className="mx_SettingsSubsectionHeading">
|
||||||
<Heading className="mx_SettingsSubsectionHeading_heading" size="4" as="h3">
|
<Heading className="mx_SettingsSubsectionHeading_heading" size="4" as={as}>
|
||||||
{heading}
|
{heading}
|
||||||
</Heading>
|
</Heading>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -48,9 +48,11 @@ exports[`DevtoolsDialog renders the devtools dialog 1`] = `
|
|||||||
class="mx_DevTools_content"
|
class="mx_DevTools_content"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<h3>
|
<h2
|
||||||
|
class="mx_DevTools_toolHeading"
|
||||||
|
>
|
||||||
Room
|
Room
|
||||||
</h3>
|
</h2>
|
||||||
<button
|
<button
|
||||||
class="mx_DevTools_button"
|
class="mx_DevTools_button"
|
||||||
>
|
>
|
||||||
@ -83,9 +85,11 @@ exports[`DevtoolsDialog renders the devtools dialog 1`] = `
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3>
|
<h2
|
||||||
|
class="mx_DevTools_toolHeading"
|
||||||
|
>
|
||||||
Other
|
Other
|
||||||
</h3>
|
</h2>
|
||||||
<button
|
<button
|
||||||
class="mx_DevTools_button"
|
class="mx_DevTools_button"
|
||||||
>
|
>
|
||||||
@ -108,9 +112,11 @@ exports[`DevtoolsDialog renders the devtools dialog 1`] = `
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3>
|
<h2
|
||||||
|
class="mx_DevTools_toolHeading"
|
||||||
|
>
|
||||||
Options
|
Options
|
||||||
</h3>
|
</h2>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsFlag"
|
class="mx_SettingsFlag"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -18,11 +18,11 @@ exports[`AppTile destroys non-persisted right panel widget on room change 1`] =
|
|||||||
<div
|
<div
|
||||||
class="mx_BaseCard_header_title"
|
class="mx_BaseCard_header_title"
|
||||||
>
|
>
|
||||||
<h4
|
<h1
|
||||||
class="mx_Heading_h4 mx_BaseCard_header_title_heading"
|
class="mx_Heading_h4 mx_BaseCard_header_title_heading"
|
||||||
>
|
>
|
||||||
Example 1
|
Example 1
|
||||||
</h4>
|
</h1>
|
||||||
<div
|
<div
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
@ -109,6 +109,7 @@ exports[`AppTile for a pinned widget should render 1`] = `
|
|||||||
data-color="1"
|
data-color="1"
|
||||||
data-testid="avatar-img"
|
data-testid="avatar-img"
|
||||||
data-type="round"
|
data-type="round"
|
||||||
|
role="img"
|
||||||
style="--cpd-avatar-size: 20px;"
|
style="--cpd-avatar-size: 20px;"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@ -122,9 +123,9 @@ exports[`AppTile for a pinned widget should render 1`] = `
|
|||||||
width="20px"
|
width="20px"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<h3>
|
<h1>
|
||||||
Example 1
|
Example 1
|
||||||
</h3>
|
</h1>
|
||||||
<span />
|
<span />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@ -221,6 +222,7 @@ exports[`AppTile for a pinned widget should render permission request 1`] = `
|
|||||||
data-color="1"
|
data-color="1"
|
||||||
data-testid="avatar-img"
|
data-testid="avatar-img"
|
||||||
data-type="round"
|
data-type="round"
|
||||||
|
role="img"
|
||||||
style="--cpd-avatar-size: 20px;"
|
style="--cpd-avatar-size: 20px;"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@ -234,9 +236,9 @@ exports[`AppTile for a pinned widget should render permission request 1`] = `
|
|||||||
width="20px"
|
width="20px"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<h3>
|
<h1>
|
||||||
Example 1
|
Example 1
|
||||||
</h3>
|
</h1>
|
||||||
<span />
|
<span />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@ -412,6 +414,7 @@ exports[`AppTile preserves non-persisted widget on container move 1`] = `
|
|||||||
data-color="1"
|
data-color="1"
|
||||||
data-testid="avatar-img"
|
data-testid="avatar-img"
|
||||||
data-type="round"
|
data-type="round"
|
||||||
|
role="img"
|
||||||
style="--cpd-avatar-size: 20px;"
|
style="--cpd-avatar-size: 20px;"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@ -425,9 +428,9 @@ exports[`AppTile preserves non-persisted widget on container move 1`] = `
|
|||||||
width="20px"
|
width="20px"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<h3>
|
<h1>
|
||||||
Example 1
|
Example 1
|
||||||
</h3>
|
</h1>
|
||||||
<span />
|
<span />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -191,6 +191,7 @@ exports[`<ExtensionsCard /> should render widgets 1`] = `
|
|||||||
data-color="1"
|
data-color="1"
|
||||||
data-testid="avatar-img"
|
data-testid="avatar-img"
|
||||||
data-type="round"
|
data-type="round"
|
||||||
|
role="img"
|
||||||
style="--cpd-avatar-size: 24px;"
|
style="--cpd-avatar-size: 24px;"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@ -231,6 +232,7 @@ exports[`<ExtensionsCard /> should render widgets 1`] = `
|
|||||||
data-color="1"
|
data-color="1"
|
||||||
data-testid="avatar-img"
|
data-testid="avatar-img"
|
||||||
data-type="round"
|
data-type="round"
|
||||||
|
role="img"
|
||||||
style="--cpd-avatar-size: 24px;"
|
style="--cpd-avatar-size: 24px;"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
|
|||||||
@ -121,11 +121,11 @@ exports[`<Notifications /> correctly handles the loading/disabled state 1`] = `
|
|||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsectionHeading"
|
class="mx_SettingsSubsectionHeading"
|
||||||
>
|
>
|
||||||
<h3
|
<h2
|
||||||
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||||
>
|
>
|
||||||
I want to be notified for (Default Setting)
|
I want to be notified for (Default Setting)
|
||||||
</h3>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsection_description"
|
class="mx_SettingsSubsection_description"
|
||||||
@ -214,11 +214,11 @@ exports[`<Notifications /> correctly handles the loading/disabled state 1`] = `
|
|||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsectionHeading"
|
class="mx_SettingsSubsectionHeading"
|
||||||
>
|
>
|
||||||
<h3
|
<h2
|
||||||
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||||
>
|
>
|
||||||
Play a sound for
|
Play a sound for
|
||||||
</h3>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsection_description"
|
class="mx_SettingsSubsection_description"
|
||||||
@ -411,11 +411,11 @@ exports[`<Notifications /> correctly handles the loading/disabled state 1`] = `
|
|||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsectionHeading"
|
class="mx_SettingsSubsectionHeading"
|
||||||
>
|
>
|
||||||
<h3
|
<h2
|
||||||
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||||
>
|
>
|
||||||
Other things we think you might be interested in:
|
Other things we think you might be interested in:
|
||||||
</h3>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsection_content"
|
class="mx_SettingsSubsection_content"
|
||||||
@ -598,11 +598,11 @@ exports[`<Notifications /> correctly handles the loading/disabled state 1`] = `
|
|||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsectionHeading"
|
class="mx_SettingsSubsectionHeading"
|
||||||
>
|
>
|
||||||
<h3
|
<h2
|
||||||
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||||
>
|
>
|
||||||
Mentions and Keywords
|
Mentions and Keywords
|
||||||
</h3>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsection_description"
|
class="mx_SettingsSubsection_description"
|
||||||
@ -1098,11 +1098,11 @@ exports[`<Notifications /> matches the snapshot 1`] = `
|
|||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsectionHeading"
|
class="mx_SettingsSubsectionHeading"
|
||||||
>
|
>
|
||||||
<h3
|
<h2
|
||||||
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||||
>
|
>
|
||||||
I want to be notified for (Default Setting)
|
I want to be notified for (Default Setting)
|
||||||
</h3>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsection_description"
|
class="mx_SettingsSubsection_description"
|
||||||
@ -1188,11 +1188,11 @@ exports[`<Notifications /> matches the snapshot 1`] = `
|
|||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsectionHeading"
|
class="mx_SettingsSubsectionHeading"
|
||||||
>
|
>
|
||||||
<h3
|
<h2
|
||||||
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||||
>
|
>
|
||||||
Play a sound for
|
Play a sound for
|
||||||
</h3>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsection_description"
|
class="mx_SettingsSubsection_description"
|
||||||
@ -1382,11 +1382,11 @@ exports[`<Notifications /> matches the snapshot 1`] = `
|
|||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsectionHeading"
|
class="mx_SettingsSubsectionHeading"
|
||||||
>
|
>
|
||||||
<h3
|
<h2
|
||||||
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||||
>
|
>
|
||||||
Other things we think you might be interested in:
|
Other things we think you might be interested in:
|
||||||
</h3>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsection_content"
|
class="mx_SettingsSubsection_content"
|
||||||
@ -1566,11 +1566,11 @@ exports[`<Notifications /> matches the snapshot 1`] = `
|
|||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsectionHeading"
|
class="mx_SettingsSubsectionHeading"
|
||||||
>
|
>
|
||||||
<h3
|
<h2
|
||||||
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
|
||||||
>
|
>
|
||||||
Mentions and Keywords
|
Mentions and Keywords
|
||||||
</h3>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsection_description"
|
class="mx_SettingsSubsection_description"
|
||||||
|
|||||||