Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/web/playwright/e2e/crypto/crypto.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
6 changes: 3 additions & 3 deletions apps/web/playwright/e2e/crypto/history-sharing.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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();

Expand Down
27 changes: 27 additions & 0 deletions apps/web/playwright/e2e/invite/invite-dialog.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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();

Expand Down
3 changes: 3 additions & 0 deletions apps/web/playwright/e2e/room/create-room.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
4 changes: 4 additions & 0 deletions apps/web/playwright/e2e/room/room-status-bar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down
21 changes: 18 additions & 3 deletions apps/web/playwright/pages/ElementAppPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
*/
public async viewRoomByName(name: string): Promise<void> {
// We get the room list by test-id which is a listbox and matching title=name
return this.page.getByTestId("room-list").locator(`[title="${name}"]`).first().click();

Check failure on line 97 in apps/web/playwright/pages/ElementAppPage.ts

View workflow job for this annotation

GitHub Actions / Run Tests [Chrome] 1/4

[Chrome] › playwright/e2e/crypto/decryption-failure-messages.spec.ts:33:9 › Cryptography › decryption failure messages › should handle device-relative historical messages @screenshot

1) [Chrome] › playwright/e2e/crypto/decryption-failure-messages.spec.ts:33:9 › Cryptography › decryption failure messages › should handle device-relative historical messages @screenshot Error: locator.click: Test timeout of 60000ms exceeded. Call log: - waiting for getByTestId('room-list').locator('[title="Test room"]').first() - locator resolved to <div title="Test room" data-testid="room-name" class="_roomName_rtaba_83">Test room</div> - attempting click action 2 × waiting for element to be visible, enabled and stable - element is visible, enabled and stable - scrolling into view if needed - done scrolling - <div class="mx_Dialog_background" data-testid="dialog-background"></div> from <div id="mx_Dialog_Container">…</div> subtree intercepts pointer events - retrying click action - waiting 20ms 2 × waiting for element to be visible, enabled and stable - element is visible, enabled and stable - scrolling into view if needed - done scrolling - <div class="mx_Dialog_background" data-testid="dialog-background"></div> from <div id="mx_Dialog_Container">…</div> subtree intercepts pointer events - retrying click action - waiting 100ms 110 × waiting for element to be visible, enabled and stable - element is visible, enabled and stable - scrolling into view if needed - done scrolling - <div class="mx_Dialog_background" data-testid="dialog-background"></div> from <div id="mx_Dialog_Container">…</div> subtree intercepts pointer events - retrying click action - waiting 500ms at ../pages/ElementAppPage.ts:97 95 | public async viewRoomByName(name: string): Promise<void> { 96 | // We get the room list by test-id which is a listbox and matching title=name > 97 | return this.page.getByTestId("room-list").locator(`[title="${name}"]`).first().click(); | ^ 98 | } 99 | 100 | /** at ElementAppPage.viewRoomByName (/home/runner/work/element-web/element-web/apps/web/playwright/pages/ElementAppPage.ts:97:88) at /home/runner/work/element-web/element-web/apps/web/playwright/e2e/crypto/decryption-failure-messages.spec.ts:50:27
}

/**
Expand Down Expand Up @@ -233,15 +233,30 @@
* Open the room info panel, and use it to send an invite to the given user.
*
* @param userId - The user to invite to the room.
* @param options - Options object
*/
public async inviteUserToCurrentRoom(userId: string): Promise<void> {
public async inviteUserToCurrentRoom(
userId: string,
options?: {
/** If true, expect and acknowledge "Confirm inviting new users" page */
confirmUnknownUser?: boolean;
},
): Promise<void> {
const rightPanel = await this.openRoomInfoPanel();
await rightPanel.getByRole("menuitem", { name: "Invite" }).click();

const input = this.page.getByRole("dialog").getByTestId("invite-dialog-input");
const dialogLocator = this.page.getByRole("dialog");
const input = dialogLocator.getByTestId("invite-dialog-input");
await input.fill(userId);
await input.press("Enter");
await this.page.getByRole("dialog").getByRole("button", { name: "Invite" }).click();
await dialogLocator.getByRole("button", { name: "Invite" }).click();

if (options?.confirmUnknownUser) {
await expect(
dialogLocator.getByRole("heading", { name: "Invite new contacts to this room?" }),
).toBeVisible();
await dialogLocator.getByRole("button", { name: "Invite" }).click();
}
}

/**
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 11 additions & 5 deletions apps/web/res/css/_common.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,7 @@ legend {
.mx_AccessSecretStorageDialog button,
.mx_InviteDialog_section button,
.mx_InviteDialog_editor button,
.mx_UnknownIdentityUsersWarningDialog button,
[class|="maplibregl"]
),
.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton, .mx_AccessibleButton),
Expand Down Expand Up @@ -625,7 +626,8 @@ legend {
.mx_ThemeChoicePanel_CustomTheme button,
.mx_UnpinAllDialog button,
.mx_ShareDialog button,
.mx_EncryptionUserSettingsTab button
.mx_EncryptionUserSettingsTab button,
.mx_UnknownIdentityUsersWarningDialog button
):last-child {
margin-right: 0px;
}
Expand All @@ -641,7 +643,8 @@ legend {
.mx_ShareDialog button,
.mx_EncryptionUserSettingsTab button,
.mx_InviteDialog_section button,
.mx_InviteDialog_editor button
.mx_InviteDialog_editor button,
.mx_UnknownIdentityUsersWarningDialog button
):focus,
.mx_Dialog input[type="submit"]:focus,
.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton, .mx_AccessibleButton):focus,
Expand All @@ -659,7 +662,8 @@ legend {
.mx_ThemeChoicePanel_CustomTheme button,
.mx_UnpinAllDialog button,
.mx_ShareDialog button,
.mx_EncryptionUserSettingsTab button
.mx_EncryptionUserSettingsTab button,
.mx_UnknownIdentityUsersWarningDialog button
),
.mx_Dialog_buttons input[type="submit"].mx_Dialog_primary {
color: var(--cpd-color-text-on-solid-primary);
Expand All @@ -678,7 +682,8 @@ legend {
.mx_ThemeChoicePanel_CustomTheme button,
.mx_UnpinAllDialog button,
.mx_ShareDialog button,
.mx_EncryptionUserSettingsTab button
.mx_EncryptionUserSettingsTab button,
.mx_UnknownIdentityUsersWarningDialog button
),
.mx_Dialog_buttons input[type="submit"].danger {
background-color: var(--cpd-color-bg-critical-primary);
Expand All @@ -701,7 +706,8 @@ legend {
.mx_ThemeChoicePanel_CustomTheme button,
.mx_UnpinAllDialog button,
.mx_ShareDialog button,
.mx_EncryptionUserSettingsTab button
.mx_EncryptionUserSettingsTab button,
.mx_UnknownIdentityUsersWarningDialog button
):disabled,
.mx_Dialog input[type="submit"]:disabled,
.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton, .mx_AccessibleButton):disabled,
Expand Down
1 change: 1 addition & 0 deletions apps/web/res/css/_components.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
@import "./views/dialogs/_UserSettingsDialog.pcss";
@import "./views/dialogs/_VerifyEMailDialog.pcss";
@import "./views/dialogs/_WidgetCapabilitiesPromptDialog.pcss";
@import "./views/dialogs/invite/_UnknownIdentityUsersWarningDialog.pcss";
@import "./views/dialogs/security/_AccessSecretStorageDialog.pcss";
@import "./views/dialogs/security/_CreateCrossSigningDialog.pcss";
@import "./views/dialogs/security/_CreateSecretStorageDialog.pcss";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
Copyright 2026 Element Creations 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.
*/

.mx_UnknownIdentityUsersWarningDialog {
display: flex;
flex-direction: column;
height: 600px; /* Consistency with InviteDialog */
}

.mx_UnknownIdentityUsersWarningDialog_headerContainer {
/* Centre the PageHeader component horizontally */
display: flex;
justify-content: center;

/* Styling for the regular text inside the header */
font: var(--cpd-font-body-lg-regular);

/* Space before the list */
padding-bottom: var(--cpd-space-6x);
}

.mx_UnknownIdentityUsersWarningDialog_userList {
width: 100%;
overflow: auto;

/* Fill available vertical space, but don't allow it to shrink to less than 60px (about the height of a single tile) */
flex: 1 0 60px;

/* Remove browser default ul padding/margin */
padding: 0;
margin: 0;
}

.mx_UnknownIdentityUsersWarningDialog_buttons {
display: flex;
gap: var(--cpd-space-4x);

> button {
flex: 1;
}
}
Loading
Loading