From 3a0cffe413e638e702a18e398d2f1430bec54c62 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 17 Feb 2025 09:10:07 +0000 Subject: [PATCH 01/15] Use EditInPlace for identity server picker. --- src/components/views/settings/SetIdServer.tsx | 76 +++++++------------ src/i18n/strings/en_EN.json | 1 + 2 files changed, 29 insertions(+), 48 deletions(-) diff --git a/src/components/views/settings/SetIdServer.tsx b/src/components/views/settings/SetIdServer.tsx index 86aa0a3df86..e9ea5dccfc2 100644 --- a/src/components/views/settings/SetIdServer.tsx +++ b/src/components/views/settings/SetIdServer.tsx @@ -9,6 +9,7 @@ Please see LICENSE files in the repository root for full details. import React, { type ReactNode } from "react"; import { logger } from "matrix-js-sdk/src/logger"; import { type IThreepid } from "matrix-js-sdk/src/matrix"; +import { EditInPlace, ErrorMessage, TooltipProvider } from "@vector-im/compound-web"; import { _t } from "../../../languageHandler"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; @@ -22,7 +23,6 @@ import { timeout } from "../../../utils/promise"; import { type ActionPayload } from "../../../dispatcher/payloads"; import InlineSpinner from "../elements/InlineSpinner"; import AccessibleButton from "../elements/AccessibleButton"; -import Field from "../elements/Field"; import QuestionDialog from "../dialogs/QuestionDialog"; import SettingsFieldset from "./SettingsFieldset"; import { SettingsSubsectionText } from "./shared/SettingsSubsection"; @@ -86,10 +86,12 @@ export default class SetIdServer extends React.Component { defaultIdServer = abbreviateUrl(getDefaultIdentityServerUrl()); } + const currentClientIdServer = MatrixClientPeg.safeGet().getIdentityServerUrl(); + this.state = { defaultIdServer, - currentClientIdServer: MatrixClientPeg.safeGet().getIdentityServerUrl(), - idServer: "", + currentClientIdServer, + idServer: currentClientIdServer ?? "", busy: false, disconnectBusy: false, checking: false, @@ -117,26 +119,7 @@ export default class SetIdServer extends React.Component { private onIdentityServerChanged = (ev: React.ChangeEvent): void => { const u = ev.target.value; - this.setState({ idServer: u }); - }; - - private getTooltip = (): JSX.Element | undefined => { - if (this.state.checking) { - return ( -
- - {_t("identity_server|checking")} -
- ); - } else if (this.state.error) { - return {this.state.error}; - } else { - return undefined; - } - }; - - private idServerChangeEnabled = (): boolean => { - return !!this.state.idServer && !this.state.busy; + this.setState({ idServer: u, error: undefined }); }; private saveIdServer = (fullUrl: string): void => { @@ -148,7 +131,7 @@ export default class SetIdServer extends React.Component { busy: false, error: undefined, currentClientIdServer: fullUrl, - idServer: "", + idServer: fullUrl, }); }; @@ -175,7 +158,7 @@ export default class SetIdServer extends React.Component { // Double check that the identity server even has terms of service. const hasTerms = await doesIdentityServerHaveTerms(MatrixClientPeg.safeGet(), fullUrl); if (!hasTerms) { - const [confirmed] = await this.showNoTermsWarning(fullUrl); + const [confirmed] = await this.showNoTermsWarning(); save = !!confirmed; } @@ -213,7 +196,7 @@ export default class SetIdServer extends React.Component { }); }; - private showNoTermsWarning(fullUrl: string): Promise<[ok?: boolean]> { + private showNoTermsWarning(): Promise<[ok?: boolean]> { const { finished } = Modal.createDialog(QuestionDialog, { title: _t("terms|identity_server_no_terms_title"), description: ( @@ -393,28 +376,25 @@ export default class SetIdServer extends React.Component { return ( -
- - - {_t("action|change")} - - {discoSection} - + this.setState((s) => ({ idServer: s.currentClientIdServer ?? "" }))} + onChange={this.onIdentityServerChanged} + onClearServerErrors={() => this.setState({ error: undefined })} + onSave={this.checkIdServer} + placeholder={this.state.defaultIdServer} + saveButtonLabel={_t("action|change")} + savedLabel={this.state.error ? undefined : _t("identity_server|changed")} + savingLabel={_t("identity_server|checking")} + serverInvalid={!!this.state.error} + value={this.state.idServer} + > + {this.state.error && {this.state.error}} + + {discoSection}
); } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d88d29008e4..7b827e0a86d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1244,6 +1244,7 @@ "change_prompt": "Disconnect from the identity server and connect to instead?", "change_server_prompt": "If you don't want to use to discover and be discoverable by existing contacts you know, enter another identity server below.", "checking": "Checking server", + "changed": "Your identity server has been changed", "description_connected": "You are currently using to discover and be discoverable by existing contacts you know. You can change your identity server below.", "description_disconnected": "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.", "description_optional": "Using an identity server is optional. If you choose not to use an identity server, you won't be discoverable by other users and you won't be able to invite others by email or phone.", From 02ffc7713e93cd036dbd42df8e65335695fe13a1 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 17 Feb 2025 09:10:16 +0000 Subject: [PATCH 02/15] Exclude picker from default dialog button styles. --- res/css/_common.pcss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/_common.pcss b/res/css/_common.pcss index fe8eff22860..1a7099ae9f0 100644 --- a/res/css/_common.pcss +++ b/res/css/_common.pcss @@ -596,6 +596,8 @@ legend { .mx_Dialog button:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]):not(.mx_AccessibleButton):not( .mx_UserProfileSettings button + ):not( + .mx_IdentityServerPicker button ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):not( .mx_EncryptionUserSettingsTab button ), From 35700c6feca141e400a7e3cfc71b3c0d7c8998b0 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 17 Feb 2025 09:12:43 +0000 Subject: [PATCH 03/15] Remove unused import. --- res/css/_common.pcss | 8 +++----- src/components/views/settings/SetIdServer.tsx | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/res/css/_common.pcss b/res/css/_common.pcss index 1a7099ae9f0..8b2c321e1b9 100644 --- a/res/css/_common.pcss +++ b/res/css/_common.pcss @@ -596,11 +596,9 @@ legend { .mx_Dialog button:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]):not(.mx_AccessibleButton):not( .mx_UserProfileSettings button - ):not( - .mx_IdentityServerPicker button - ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):not( - .mx_EncryptionUserSettingsTab button - ), + ):not(.mx_IdentityServerPicker button):not(.mx_ThemeChoicePanel_CustomTheme button):not( + .mx_UnpinAllDialog button + ):not(.mx_ShareDialog button):not(.mx_EncryptionUserSettingsTab button), .mx_Dialog input[type="submit"], .mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton), .mx_Dialog_buttons input[type="submit"] { diff --git a/src/components/views/settings/SetIdServer.tsx b/src/components/views/settings/SetIdServer.tsx index e9ea5dccfc2..4b2f888e60c 100644 --- a/src/components/views/settings/SetIdServer.tsx +++ b/src/components/views/settings/SetIdServer.tsx @@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details. import React, { type ReactNode } from "react"; import { logger } from "matrix-js-sdk/src/logger"; import { type IThreepid } from "matrix-js-sdk/src/matrix"; -import { EditInPlace, ErrorMessage, TooltipProvider } from "@vector-im/compound-web"; +import { EditInPlace, ErrorMessage } from "@vector-im/compound-web"; import { _t } from "../../../languageHandler"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; From 574b6db15ec9117320b682068fd88f23b2cca0d8 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 17 Feb 2025 09:16:31 +0000 Subject: [PATCH 04/15] Update test --- src/i18n/strings/en_EN.json | 2 +- .../SecurityUserSettingsTab-test.tsx.snap | 36 +++++++++---------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 7b827e0a86d..66ebfe4cffb 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1243,8 +1243,8 @@ "change": "Change identity server", "change_prompt": "Disconnect from the identity server and connect to instead?", "change_server_prompt": "If you don't want to use to discover and be discoverable by existing contacts you know, enter another identity server below.", - "checking": "Checking server", "changed": "Your identity server has been changed", + "checking": "Checking server", "description_connected": "You are currently using to discover and be discoverable by existing contacts you know. You can change your identity server below.", "description_disconnected": "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.", "description_optional": "Using an identity server is optional. If you choose not to use an identity server, you won't be discoverable by other users and you won't be able to invite others by email or phone.", diff --git a/test/unit-tests/components/views/settings/tabs/user/__snapshots__/SecurityUserSettingsTab-test.tsx.snap b/test/unit-tests/components/views/settings/tabs/user/__snapshots__/SecurityUserSettingsTab-test.tsx.snap index 3aac2e2c026..1bd9fc48fa7 100644 --- a/test/unit-tests/components/views/settings/tabs/user/__snapshots__/SecurityUserSettingsTab-test.tsx.snap +++ b/test/unit-tests/components/views/settings/tabs/user/__snapshots__/SecurityUserSettingsTab-test.tsx.snap @@ -438,33 +438,29 @@ exports[` renders security section 1`] = ` class="mx_SettingsFieldset_content" >
- -
-
- Change +
+ +
From 3f39189431bec1f476c9aaacd37b87f42c1b540d Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 17 Feb 2025 09:43:02 +0000 Subject: [PATCH 05/15] Remove unused css --- res/css/_components.pcss | 1 - res/css/views/settings/_SetIdServer.pcss | 23 ----------------------- 2 files changed, 24 deletions(-) delete mode 100644 res/css/views/settings/_SetIdServer.pcss diff --git a/res/css/_components.pcss b/res/css/_components.pcss index e6ca0c7ae9a..56669a4a511 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -348,7 +348,6 @@ @import "./views/settings/_PowerLevelSelector.pcss"; @import "./views/settings/_RoomProfileSettings.pcss"; @import "./views/settings/_SecureBackupPanel.pcss"; -@import "./views/settings/_SetIdServer.pcss"; @import "./views/settings/_SetIntegrationManager.pcss"; @import "./views/settings/_SettingsFieldset.pcss"; @import "./views/settings/_SettingsHeader.pcss"; diff --git a/res/css/views/settings/_SetIdServer.pcss b/res/css/views/settings/_SetIdServer.pcss deleted file mode 100644 index 377292451ff..00000000000 --- a/res/css/views/settings/_SetIdServer.pcss +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2019-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. -*/ - -.mx_SetIdServer { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: $spacing-8; - - .mx_Field { - width: 100%; - margin: 0; - } -} - -.mx_SetIdServer_tooltip { - max-width: var(--SettingsTab_tooltip-max-width); -} From 48e3445ef902b79e73319f53b15a820ef3141f07 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 17 Feb 2025 09:43:05 +0000 Subject: [PATCH 06/15] Update test --- playwright/e2e/settings/security-user-settings-tab.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playwright/e2e/settings/security-user-settings-tab.spec.ts b/playwright/e2e/settings/security-user-settings-tab.spec.ts index 9b9439796d6..b8792a6c7f6 100644 --- a/playwright/e2e/settings/security-user-settings-tab.spec.ts +++ b/playwright/e2e/settings/security-user-settings-tab.spec.ts @@ -41,10 +41,10 @@ test.describe("Security user settings tab", () => { }); }); - test("should contain section to set ID server", async ({ app }) => { + test.only("should contain section to set ID server", async ({ app }) => { const tab = await app.settings.openUserSettings("Security"); - const setIdServer = tab.locator(".mx_SetIdServer"); + const setIdServer = tab.locator(".mx_IdentityServerPicker"); await setIdServer.scrollIntoViewIfNeeded(); // Assert that an input area for identity server exists await expect(setIdServer.getByRole("textbox", { name: "Enter a new identity server" })).toBeVisible(); From a2c411d0583cfe583acd21da8210e8cd6a908bc1 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 17 Feb 2025 09:45:52 +0000 Subject: [PATCH 07/15] drop only --- playwright/e2e/settings/security-user-settings-tab.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright/e2e/settings/security-user-settings-tab.spec.ts b/playwright/e2e/settings/security-user-settings-tab.spec.ts index b8792a6c7f6..43a6c051fe4 100644 --- a/playwright/e2e/settings/security-user-settings-tab.spec.ts +++ b/playwright/e2e/settings/security-user-settings-tab.spec.ts @@ -41,7 +41,7 @@ test.describe("Security user settings tab", () => { }); }); - test.only("should contain section to set ID server", async ({ app }) => { + test("should contain section to set ID server", async ({ app }) => { const tab = await app.settings.openUserSettings("Security"); const setIdServer = tab.locator(".mx_IdentityServerPicker"); From 29647c90faa8d4ad4d3e52a57ab7e5c1f78bf731 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 17 Feb 2025 11:14:33 +0000 Subject: [PATCH 08/15] Add a test for setting an ID server. --- .../security-user-settings-tab.spec.ts | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/playwright/e2e/settings/security-user-settings-tab.spec.ts b/playwright/e2e/settings/security-user-settings-tab.spec.ts index 43a6c051fe4..1a715025a58 100644 --- a/playwright/e2e/settings/security-user-settings-tab.spec.ts +++ b/playwright/e2e/settings/security-user-settings-tab.spec.ts @@ -25,7 +25,7 @@ test.describe("Security user settings tab", () => { }, }); - test.beforeEach(async ({ page, app, user }) => { + test.beforeEach(async ({ page, app }) => { // Dismiss "Notification" toast await app.closeNotificationToast(); await page.locator(".mx_Toast_buttons").getByRole("button", { name: "Yes" }).click(); // Allow analytics @@ -41,13 +41,54 @@ test.describe("Security user settings tab", () => { }); }); - test("should contain section to set ID server", async ({ app }) => { + test("should be able to set an ID server", async ({ app, context, user, page }) => { const tab = await app.settings.openUserSettings("Security"); + await context.route("https://identity.example.org/_matrix/identity/v2", async (route) => { + await route.fulfill({ + status: 200, + json: {}, + }); + }); + await context.route("https://identity.example.org/_matrix/identity/v2/account/register", async (route) => { + await route.fulfill({ + status: 200, + json: { + token: "AToken", + }, + }); + }); + await context.route("https://identity.example.org/_matrix/identity/v2/account", async (route) => { + await route.fulfill({ + status: 200, + json: { + user_id: user.userId, + }, + }); + }); + await context.route("https://identity.example.org/_matrix/identity/v2/terms", async (route) => { + await route.fulfill({ + status: 200, + json: { + policies: {}, + }, + }); + }); const setIdServer = tab.locator(".mx_IdentityServerPicker"); await setIdServer.scrollIntoViewIfNeeded(); - // Assert that an input area for identity server exists - await expect(setIdServer.getByRole("textbox", { name: "Enter a new identity server" })).toBeVisible(); + + const textElement = setIdServer.getByRole("textbox", { name: "Enter a new identity server" }); + await textElement.click(); + await textElement.fill("https://identity.example.org"); + await setIdServer.getByRole("button", { name: "Change" }).click(); + + await expect(setIdServer.getByText("Checking server")).toBeVisible(); + // Accept terms + await page.getByTestId("dialog-primary-button").click(); + // Check identity has changed. + await expect(setIdServer.getByText("Your identity server has been changed")).toBeVisible(); + // Ensure section title is updated. + await expect(tab.getByText(`Identity server (identity.example.org)`, { exact: true })).toBeVisible(); }); test("should enable show integrations as enabled", async ({ app, page }) => { From 24a04530e202f6576a12fe74577245459d62d5de Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 17 Feb 2025 12:32:48 +0000 Subject: [PATCH 09/15] Add a unit test for SetIdServer --- .../views/settings/SetIdServer-test.tsx | 102 ++++++++++++++++++ .../__snapshots__/SetIdServer-test.tsx.snap | 54 ++++++++++ 2 files changed, 156 insertions(+) create mode 100644 test/unit-tests/components/views/settings/SetIdServer-test.tsx create mode 100644 test/unit-tests/components/views/settings/__snapshots__/SetIdServer-test.tsx.snap diff --git a/test/unit-tests/components/views/settings/SetIdServer-test.tsx b/test/unit-tests/components/views/settings/SetIdServer-test.tsx new file mode 100644 index 00000000000..7e926530ea7 --- /dev/null +++ b/test/unit-tests/components/views/settings/SetIdServer-test.tsx @@ -0,0 +1,102 @@ +/* +Copyright 2024 New Vector Ltd. +Copyright 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 React from "react"; +import { render, waitFor } from "jest-matrix-react"; +import userEvent from "@testing-library/user-event"; +import fetchMock from "fetch-mock-jest"; + +import SetIdServer from "../../../../../src/components/views/settings/SetIdServer"; +import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext"; +import { getMockClientWithEventEmitter, mockClientMethodsUser, mockClientMethodsServer } from "../../../../test-utils"; + +describe("", () => { + const userId = "@alice:server.org"; + + const mockClient = getMockClientWithEventEmitter({ + ...mockClientMethodsUser(userId), + ...mockClientMethodsServer(), + getOpenIdToken: jest.fn().mockResolvedValue("a_token"), + getTerms: jest.fn(), + setAccountData: jest.fn(), + }); + + const getComponent = () => ( + + + + ); + + afterAll(() => { + jest.resetAllMocks(); + }); + + it("renders expected fields", () => { + const { asFragment } = render(getComponent()); + expect(asFragment()).toMatchSnapshot(); + }); + + it("should allow setting an identity server", async () => { + const { getByLabelText, getByRole } = render(getComponent()); + + fetchMock.get("https://identity.example.org/_matrix/identity/v2", { + body: {}, + }); + fetchMock.get("https://identity.example.org/_matrix/identity/v2/account", { + body: { user_id: userId }, + }); + fetchMock.post("https://identity.example.org/_matrix/identity/v2/account/register", { + body: { token: "foobar" }, + }); + + const identServerField = getByLabelText("Enter a new identity server"); + await userEvent.type(identServerField, "https://identity.example.org"); + await userEvent.click(getByRole("button", { name: "Change" })); + await userEvent.click(getByRole("button", { name: "Continue" })); + }); + + it("should clear input on cancel", async () => { + const { getByLabelText, getByRole } = render(getComponent()); + const identServerField = getByLabelText("Enter a new identity server"); + await userEvent.type(identServerField, "https://identity.example.org"); + await userEvent.click(getByRole("button", { name: "Reset" })); + expect((identServerField as HTMLInputElement).value).toEqual(""); + }); + + it("should show error when an error occurs", async () => { + const { getByLabelText, getByRole, getByText } = render(getComponent()); + + fetchMock.get("https://invalid.example.org/_matrix/identity/v2", { + body: {}, + status: 404, + }); + fetchMock.get("https://invalid.example.org/_matrix/identity/v2/account", { + body: {}, + status: 404, + }); + fetchMock.post("https://invalid.example.org/_matrix/identity/v2/account/register", { + body: {}, + status: 404, + }); + + const identServerField = getByLabelText("Enter a new identity server"); + await userEvent.type(identServerField, "https://invalid.example.org"); + await userEvent.click(getByRole("button", { name: "Change" })); + + await waitFor( + () => { + expect(getByText("Not a valid identity server (status code 404)")).toBeVisible(); + }, + { timeout: 3000 }, + ); + + // Check the error vanishes when the input is edited. + await userEvent.type(identServerField, "https://identity2.example.org"); + expect(() => getByText("Not a valid identity server (status code 404)")).toThrow(); + }); +}); diff --git a/test/unit-tests/components/views/settings/__snapshots__/SetIdServer-test.tsx.snap b/test/unit-tests/components/views/settings/__snapshots__/SetIdServer-test.tsx.snap new file mode 100644 index 00000000000..cc3af24e3c8 --- /dev/null +++ b/test/unit-tests/components/views/settings/__snapshots__/SetIdServer-test.tsx.snap @@ -0,0 +1,54 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders expected fields 1`] = ` + +
+ + Identity server + +
+
+ You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below. +
+
+
+
+
+ +
+ +
+
+
+
+
+
+`; From 73d382d9eb4931098aeca6ab2e2667e3a196fac0 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 17 Feb 2025 14:28:49 +0000 Subject: [PATCH 10/15] fix tests --- playwright/e2e/settings/security-user-settings-tab.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/playwright/e2e/settings/security-user-settings-tab.spec.ts b/playwright/e2e/settings/security-user-settings-tab.spec.ts index 1a715025a58..9a5616ef597 100644 --- a/playwright/e2e/settings/security-user-settings-tab.spec.ts +++ b/playwright/e2e/settings/security-user-settings-tab.spec.ts @@ -25,14 +25,14 @@ test.describe("Security user settings tab", () => { }, }); - test.beforeEach(async ({ page, app }) => { + test.beforeEach(async ({ page, app, user }) => { // Dismiss "Notification" toast await app.closeNotificationToast(); await page.locator(".mx_Toast_buttons").getByRole("button", { name: "Yes" }).click(); // Allow analytics }); test.describe("AnalyticsLearnMoreDialog", () => { - test("should be rendered properly", { tag: "@screenshot" }, async ({ app, page }) => { + test("should be rendered properly", { tag: "@screenshot" }, async ({ app, page, user }) => { const tab = await app.settings.openUserSettings("Security"); await tab.getByRole("button", { name: "Learn more" }).click(); await expect(page.locator(".mx_AnalyticsLearnMoreDialog_wrapper .mx_Dialog")).toMatchScreenshot( @@ -91,7 +91,7 @@ test.describe("Security user settings tab", () => { await expect(tab.getByText(`Identity server (identity.example.org)`, { exact: true })).toBeVisible(); }); - test("should enable show integrations as enabled", async ({ app, page }) => { + test("should enable show integrations as enabled", async ({ app, page, user }) => { const tab = await app.settings.openUserSettings("Security"); const setIntegrationManager = tab.locator(".mx_SetIntegrationManager"); From 397b79200de2f1c5915540d7b326aebc67c088ed Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 18 Feb 2025 09:42:50 +0000 Subject: [PATCH 11/15] Reformat mx_Dialog button :not list to use a more readable selector. --- res/css/_common.pcss | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/res/css/_common.pcss b/res/css/_common.pcss index 8b2c321e1b9..6e26757e480 100644 --- a/res/css/_common.pcss +++ b/res/css/_common.pcss @@ -589,18 +589,21 @@ legend { * in the app look the same by being AccessibleButtons, or possibly by having explict button classes. * We should go through and have one consistent set of styles for buttons throughout the app. * For now, I am duplicating the selectors here for mx_Dialog and mx_DialogButtons. - * - * Elements that should not be styled like a dialog button are mentioned in a :not() pseudo-class. - * For the widest browser support, we use multiple :not pseudo-classes instead of :not(.a, .b). */ .mx_Dialog - button:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]):not(.mx_AccessibleButton):not( - .mx_UserProfileSettings button - ):not(.mx_IdentityServerPicker button):not(.mx_ThemeChoicePanel_CustomTheme button):not( - .mx_UnpinAllDialog button - ):not(.mx_ShareDialog button):not(.mx_EncryptionUserSettingsTab button), + button:not( + .mx_EncryptionUserSettingsTab button, + .mx_UserProfileSettings button, + .mx_ShareDialog button, + .mx_UnpinAllDialog button, + .mx_ThemeChoicePanel_CustomTheme button, + .mx_Dialog_nonDialogButton, + .mx_AccessibleButton, + .mx_IdentityServerPicker button, + [class|="maplibregl"] + ), +.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton, .mx_AccessibleButton), .mx_Dialog input[type="submit"], -.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton), .mx_Dialog_buttons input[type="submit"] { @mixin mx_DialogButton; margin-left: 0px; From 5ed4e100caf7e32d2e37d82953d92d2fb272627d Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 18 Feb 2025 09:49:02 +0000 Subject: [PATCH 12/15] Reformat other :not sections --- res/css/_common.pcss | 60 +++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/res/css/_common.pcss b/res/css/_common.pcss index 6e26757e480..cfe77aa4a64 100644 --- a/res/css/_common.pcss +++ b/res/css/_common.pcss @@ -619,32 +619,43 @@ legend { } .mx_Dialog - button:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]):not(.mx_AccessibleButton):not( - .mx_UserProfileSettings button - ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):not( + button:not( + .mx_Dialog_nonDialogButton, + [class|="maplibregl"], + .mx_AccessibleButton .mx_UserProfileSettings button, + .mx_ThemeChoicePanel_CustomTheme button, + .mx_UnpinAllDialog button, + .mx_ShareDialog button, .mx_EncryptionUserSettingsTab button ):last-child { margin-right: 0px; } .mx_Dialog - button:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]):not(.mx_AccessibleButton):not( - .mx_UserProfileSettings button - ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):not( + button:not( + .mx_Dialog_nonDialogButton, + [class|="maplibregl"], + .mx_AccessibleButton .mx_UserProfileSettings button, + .mx_ThemeChoicePanel_CustomTheme button, + .mx_UnpinAllDialog button, + .mx_ShareDialog button, .mx_EncryptionUserSettingsTab button ):focus, .mx_Dialog input[type="submit"]:focus, -.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):focus, +.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton, .mx_AccessibleButton):focus, .mx_Dialog_buttons input[type="submit"]:focus { filter: brightness($focus-brightness); } -.mx_Dialog button.mx_Dialog_primary:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]), +.mx_Dialog button.mx_Dialog_primary:not(.mx_Dialog_nonDialogButton, [class|="maplibregl"]), .mx_Dialog input[type="submit"].mx_Dialog_primary, .mx_Dialog_buttons - button.mx_Dialog_primary:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):not( - .mx_UserProfileSettings button - ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):not( + button:not( + .mx_Dialog_nonDialogButton, + .mx_AccessibleButton .mx_UserProfileSettings button, + .mx_ThemeChoicePanel_CustomTheme button, + .mx_UnpinAllDialog button, + .mx_ShareDialog button, .mx_EncryptionUserSettingsTab button ), .mx_Dialog_buttons input[type="submit"].mx_Dialog_primary { @@ -654,32 +665,41 @@ legend { min-width: 156px; } -.mx_Dialog button.danger:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]), +.mx_Dialog button.danger:not(.mx_Dialog_nonDialogButton, [class|="maplibregl"]), .mx_Dialog input[type="submit"].danger, .mx_Dialog_buttons - button.danger:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):not(.mx_UserProfileSettings button):not( - .mx_ThemeChoicePanel_CustomTheme button - ):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):not(.mx_EncryptionUserSettingsTab button), + button.danger:not( + .mx_Dialog_nonDialogButton, + .mx_AccessibleButton .mx_UserProfileSettings button, + .mx_ThemeChoicePanel_CustomTheme button, + .mx_UnpinAllDialog button, + .mx_ShareDialog button, + .mx_EncryptionUserSettingsTab button + ), .mx_Dialog_buttons input[type="submit"].danger { background-color: var(--cpd-color-bg-critical-primary); border: solid 1px var(--cpd-color-bg-critical-primary); color: var(--cpd-color-text-on-solid-primary); } -.mx_Dialog button.warning:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]), +.mx_Dialog button.warning:not(.mx_Dialog_nonDialogButton, [class|="maplibregl"]), .mx_Dialog input[type="submit"].warning { border: solid 1px var(--cpd-color-border-critical-subtle); color: var(--cpd-color-text-critical-primary); } .mx_Dialog - button:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]):not(.mx_AccessibleButton):not( - .mx_UserProfileSettings button - ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):not( + button:not( + .mx_Dialog_nonDialogButton, + [class|="maplibregl"], + .mx_AccessibleButton .mx_UserProfileSettings button, + .mx_ThemeChoicePanel_CustomTheme button, + .mx_UnpinAllDialog button, + .mx_ShareDialog button, .mx_EncryptionUserSettingsTab button ):disabled, .mx_Dialog input[type="submit"]:disabled, -.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):disabled, +.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton, .mx_AccessibleButton):disabled, .mx_Dialog_buttons input[type="submit"]:disabled { background-color: $light-fg-color; border: solid 1px $light-fg-color; From 84e698e7a9227f9d66bae0b0e44509a268a7bf65 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 18 Feb 2025 09:56:23 +0000 Subject: [PATCH 13/15] forgot a comma --- res/css/_common.pcss | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/res/css/_common.pcss b/res/css/_common.pcss index cfe77aa4a64..75180013f62 100644 --- a/res/css/_common.pcss +++ b/res/css/_common.pcss @@ -622,7 +622,8 @@ legend { button:not( .mx_Dialog_nonDialogButton, [class|="maplibregl"], - .mx_AccessibleButton .mx_UserProfileSettings button, + .mx_AccessibleButton, + .mx_UserProfileSettings button, .mx_ThemeChoicePanel_CustomTheme button, .mx_UnpinAllDialog button, .mx_ShareDialog button, @@ -635,7 +636,8 @@ legend { button:not( .mx_Dialog_nonDialogButton, [class|="maplibregl"], - .mx_AccessibleButton .mx_UserProfileSettings button, + .mx_AccessibleButton, + .mx_UserProfileSettings button, .mx_ThemeChoicePanel_CustomTheme button, .mx_UnpinAllDialog button, .mx_ShareDialog button, @@ -652,7 +654,8 @@ legend { .mx_Dialog_buttons button:not( .mx_Dialog_nonDialogButton, - .mx_AccessibleButton .mx_UserProfileSettings button, + .mx_AccessibleButton, + .mx_UserProfileSettings button, .mx_ThemeChoicePanel_CustomTheme button, .mx_UnpinAllDialog button, .mx_ShareDialog button, @@ -670,7 +673,8 @@ legend { .mx_Dialog_buttons button.danger:not( .mx_Dialog_nonDialogButton, - .mx_AccessibleButton .mx_UserProfileSettings button, + .mx_AccessibleButton, + .mx_UserProfileSettings button, .mx_ThemeChoicePanel_CustomTheme button, .mx_UnpinAllDialog button, .mx_ShareDialog button, @@ -692,7 +696,8 @@ legend { button:not( .mx_Dialog_nonDialogButton, [class|="maplibregl"], - .mx_AccessibleButton .mx_UserProfileSettings button, + .mx_AccessibleButton, + .mx_UserProfileSettings button, .mx_ThemeChoicePanel_CustomTheme button, .mx_UnpinAllDialog button, .mx_ShareDialog button, From 1fa1048463561de2418ab00718f604895af2d8a4 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 18 Feb 2025 10:25:35 +0000 Subject: [PATCH 14/15] We're in 2025 now. --- test/unit-tests/components/views/settings/SetIdServer-test.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/unit-tests/components/views/settings/SetIdServer-test.tsx b/test/unit-tests/components/views/settings/SetIdServer-test.tsx index 7e926530ea7..d92b7c27377 100644 --- a/test/unit-tests/components/views/settings/SetIdServer-test.tsx +++ b/test/unit-tests/components/views/settings/SetIdServer-test.tsx @@ -1,6 +1,5 @@ /* -Copyright 2024 New Vector Ltd. -Copyright 2023 The Matrix.org Foundation C.I.C. +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. From 80e5cfa39669a3e7abc079d3aba1fde11520bc96 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 18 Feb 2025 10:33:19 +0000 Subject: [PATCH 15/15] Update copyright + use class methods. --- src/components/views/settings/SetIdServer.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/views/settings/SetIdServer.tsx b/src/components/views/settings/SetIdServer.tsx index 4b2f888e60c..520c642172a 100644 --- a/src/components/views/settings/SetIdServer.tsx +++ b/src/components/views/settings/SetIdServer.tsx @@ -1,5 +1,5 @@ /* -Copyright 2024 New Vector Ltd. +Copyright 2024-2025 New Vector Ltd. Copyright 2019-2021 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial @@ -330,6 +330,9 @@ export default class SetIdServer extends React.Component { }); }; + private onInputCancel = (): void => this.setState((s) => ({ idServer: s.currentClientIdServer ?? "" })); + private onClearServerErrors = (): void => this.setState({ error: undefined }); + public render(): React.ReactNode { const idServerUrl = this.state.currentClientIdServer; let sectionTitle; @@ -339,13 +342,13 @@ export default class SetIdServer extends React.Component { bodyText = _t( "identity_server|description_connected", {}, - { server: (sub) => {abbreviateUrl(idServerUrl)} }, + { server: () => {abbreviateUrl(idServerUrl)} }, ); if (this.props.missingTerms) { bodyText = _t( "identity_server|change_server_prompt", {}, - { server: (sub) => {abbreviateUrl(idServerUrl)} }, + { server: () => {abbreviateUrl(idServerUrl)} }, ); } } else { @@ -381,9 +384,9 @@ export default class SetIdServer extends React.Component { cancelButtonLabel={_t("action|reset")} disabled={!!this.state.busy} label={_t("identity_server|url_field_label")} - onCancel={() => this.setState((s) => ({ idServer: s.currentClientIdServer ?? "" }))} + onCancel={this.onInputCancel} onChange={this.onIdentityServerChanged} - onClearServerErrors={() => this.setState({ error: undefined })} + onClearServerErrors={this.onClearServerErrors} onSave={this.checkIdServer} placeholder={this.state.defaultIdServer} saveButtonLabel={_t("action|change")}