Confirm before inviting unknown users to a DM/room (#33171)

* InviteDialog: factor out startDmOrSendInvites

Factor out the logic of calling `startDm` or `inviteUsers` to a helper
function. We're going to need to call this from a second location soon, so this
is useful groundwork.

* Add `UnknownIdentityUsersWarningDialog`

* Add unit tests

* Update playwright tests

* Convert if/else to switch statement

* Convert helper functions to React components

* Factor out "onRemove" callback

* Add clarifying comment
This commit is contained in:
Richard van der Hoff
2026-04-22 20:05:31 +00:00
committed by GitHub
parent f4c62abbcd
commit cd515444a8
18 changed files with 511 additions and 27 deletions
@@ -27,6 +27,9 @@ const startDMWithBob = async (page: Page, bob: Bot) => {
await page.getByRole("option", { name: bob.credentials.displayName }).click();
await expect(page.getByTestId("invite-dialog-input-wrapper").getByText("Bob")).toBeVisible();
await page.getByRole("button", { name: "Go" }).click();
await expect(page.getByRole("heading", { name: "Start a chat with this new contact?" })).toBeVisible();
await page.getByRole("button", { name: "Continue" }).click();
};
const testMessages = async (page: Page, bob: Bot, bobRoomId: string) => {
@@ -49,7 +49,7 @@ test.describe("History sharing", function () {
await sendMessageInCurrentRoom(alicePage, "A message from Alice");
// Send the invite to Bob
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId, { confirmUnknownUser: true });
// Bob accepts the invite
await bobPage.getByRole("option", { name: "TestRoom" }).click();
@@ -105,7 +105,7 @@ test.describe("History sharing", function () {
// Alice invites Bob, and Bob accepts
const roomId = await aliceElementApp.getCurrentRoomIdFromUrl();
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId, { confirmUnknownUser: true });
await bobPage.getByRole("option", { name: "TestRoom" }).click();
await bobPage.getByRole("button", { name: "Accept" }).click();
@@ -143,7 +143,7 @@ test.describe("History sharing", function () {
await sendMessageInCurrentRoom(bobPage, "Message3: 'shared' visibility, but Bob thinks it is still 'joined'");
// Alice now invites Charlie
await aliceElementApp.inviteUserToCurrentRoom(charlieCredentials.userId);
await aliceElementApp.inviteUserToCurrentRoom(charlieCredentials.userId, { confirmUnknownUser: true });
await charliePage.getByRole("option", { name: "TestRoom" }).click();
await charliePage.getByRole("button", { name: "Accept" }).click();
@@ -9,6 +9,15 @@ Please see LICENSE files in the repository root for full details.
import { test, expect } from "../../element-web-test";
/**
* CSS which will hide the mxid in the user list of the "unknown users" confirmation dialog. This is useful because the
* MXID is not stable and the screenshot tests will otherwise fail.
*
* Ideally RichItem would give us a way to do this that doesn't involve gnarly CSS.
*/
const UNKNOWN_IDENTITY_USERS_DIALOG_HIDE_MXID_CSS =
'[data-testid="userlist"] li > span:nth-last-child(1) { display: none }';
test.describe("Invite dialog", function () {
test.use({
displayName: "Hanako",
@@ -62,6 +71,15 @@ test.describe("Invite dialog", function () {
// Invite the bot
await other.getByRole("button", { name: "Invite" }).click();
// Expect a confirmation dialog, screenshot, and dismiss
await expect(
page.locator(".mx_Dialog").getByRole("heading", { name: "Invite new contacts to this room?" }),
).toBeVisible();
await expect(page.locator(".mx_Dialog")).toMatchScreenshot("confirm-invite-new-contact.png", {
css: UNKNOWN_IDENTITY_USERS_DIALOG_HIDE_MXID_CSS,
});
await page.locator(".mx_Dialog").getByRole("button", { name: "Invite" }).click();
// Assert that the invite dialog disappears
await expect(page.locator(".mx_InviteDialog_other")).not.toBeVisible();
@@ -104,6 +122,15 @@ test.describe("Invite dialog", function () {
// Open a direct message UI
await other.getByRole("button", { name: "Go" }).click();
// Expect a confirmation dialog, screenshot, and dismiss
await expect(
page.locator(".mx_Dialog").getByRole("heading", { name: "Start a chat with this new contact?" }),
).toBeVisible();
await expect(page.locator(".mx_Dialog")).toMatchScreenshot("confirm-chat-with-new-contact.png", {
css: UNKNOWN_IDENTITY_USERS_DIALOG_HIDE_MXID_CSS,
});
await page.locator(".mx_Dialog").getByRole("button", { name: "Continue" }).click();
// Assert that the invite dialog disappears
await expect(page.locator(".mx_InviteDialog_other")).not.toBeVisible();
@@ -57,6 +57,9 @@ test.describe("Create Room", () => {
await page.getByRole("button", { name: "Go" }).click();
await expect(page.getByRole("heading", { name: "Start a chat with this new contact?" })).toBeVisible();
await page.getByRole("button", { name: "Continue" }).click();
await expect(page.getByText("Encryption enabled")).toBeVisible();
await expect(page.getByText("Send your first message to")).toBeVisible();
@@ -163,6 +163,10 @@ test.describe("Room Status Bar", () => {
).toBeVisible();
await other.getByRole("option", { name: "Alice" }).click();
await other.getByRole("button", { name: "Go" }).click();
await expect(page.getByRole("heading", { name: "Start a chat with this new contact?" })).toBeVisible();
await page.getByRole("button", { name: "Continue" }).click();
// Send a message to invite the bots
const composer = app.getComposerField();
await composer.fill("Hello");
@@ -33,7 +33,7 @@ test.describe("Other people's devices section in Encryption tab", () => {
// Create the room and invite bob
await createRoom(alicePage, "TestRoom", true);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId, { confirmUnknownUser: true });
// Bob accepts the invite
await bobPage.getByRole("option", { name: "TestRoom" }).click();
@@ -72,7 +72,7 @@ test.describe("Other people's devices section in Encryption tab", () => {
// Create the room and invite bob
await createRoom(alicePage, "TestRoom", true);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId, { confirmUnknownUser: true });
// Bob accepts the invite
await bobPage.getByRole("option", { name: "TestRoom" }).click();
@@ -115,7 +115,7 @@ test.describe("Other people's devices section in Encryption tab", () => {
// Create the room and invite bob
await createRoom(alicePage, "TestRoom", true);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId, { confirmUnknownUser: true });
// Bob accepts the invite and dismisses the warnings.
await bobPage.getByRole("option", { name: "TestRoom" }).click();
@@ -149,7 +149,7 @@ test.describe("Other people's devices section in Encryption tab", () => {
// Alice creates the room and invite Bob.
await createRoom(alicePage, "TestRoom", true);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId, { confirmUnknownUser: true });
// Bob accepts the invite.
await bobPage.getByRole("option", { name: "TestRoom" }).click();
@@ -214,7 +214,7 @@ test.describe("Other people's devices section in Encryption tab", () => {
// Alice creates the room and invite Bob.
await createRoom(alicePage, "TestRoom", true);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId, { confirmUnknownUser: true });
// Bob accepts the invite.
await bobPage.getByRole("option", { name: "TestRoom" }).click();