From 83e6a6057d613d64cfc5ec5d65757e214f4dc095 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Wed, 19 Apr 2023 13:34:27 +0200 Subject: [PATCH] Add developer tools option to room list context menu (#10635) * Make developer tools more accessible * Extend tests * Use settings hook * Trigger CI --- .../context_menus/_IconizedContextMenu.pcss | 6 ++- res/css/views/rooms/_RoomTile.pcss | 6 +-- .../context_menus/DeveloperToolsOption.tsx | 47 +++++++++++++++++++ .../views/context_menus/RoomContextMenu.tsx | 25 +++------- .../context_menus/RoomGeneralContextMenu.tsx | 13 ++++- .../tabs/user/HelpUserSettingsTab.tsx | 2 +- .../context_menus/RoomContextMenu-test.tsx | 26 ++++++++-- .../RoomGeneralContextMenu-test.tsx | 33 ++++++++++++- 8 files changed, 126 insertions(+), 32 deletions(-) create mode 100644 src/components/views/context_menus/DeveloperToolsOption.tsx diff --git a/res/css/views/context_menus/_IconizedContextMenu.pcss b/res/css/views/context_menus/_IconizedContextMenu.pcss index e6600620ac..29b2eef7a4 100644 --- a/res/css/views/context_menus/_IconizedContextMenu.pcss +++ b/res/css/views/context_menus/_IconizedContextMenu.pcss @@ -1,5 +1,5 @@ /* -Copyright 2020 The Matrix.org Foundation C.I.C. +Copyright 2020 - 2023 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -180,6 +180,10 @@ limitations under the License. margin-right: -5px; } + .mx_IconizedContextMenu_developerTools::before { + mask-image: url("$(res)/img/element-icons/settings/flask.svg"); + } + .mx_IconizedContextMenu_checked::before { mask-image: url("$(res)/img/element-icons/roomlist/checkmark.svg"); } diff --git a/res/css/views/rooms/_RoomTile.pcss b/res/css/views/rooms/_RoomTile.pcss index c8bc6b2c92..700c55b046 100644 --- a/res/css/views/rooms/_RoomTile.pcss +++ b/res/css/views/rooms/_RoomTile.pcss @@ -1,5 +1,5 @@ /* -Copyright 2020 The Matrix.org Foundation C.I.C. +Copyright 2020 - 2023 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -235,10 +235,6 @@ limitations under the License. mask-image: url("$(res)/img/element-icons/export.svg"); } - .mx_RoomTile_iconDeveloperTools::before { - mask-image: url("$(res)/img/element-icons/settings/flask.svg"); - } - .mx_RoomTile_iconCopyLink::before { mask-image: url("$(res)/img/element-icons/link.svg"); } diff --git a/src/components/views/context_menus/DeveloperToolsOption.tsx b/src/components/views/context_menus/DeveloperToolsOption.tsx new file mode 100644 index 0000000000..9b483c35d6 --- /dev/null +++ b/src/components/views/context_menus/DeveloperToolsOption.tsx @@ -0,0 +1,47 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from "react"; + +import Modal from "../../../Modal"; +import DevtoolsDialog from "../dialogs/DevtoolsDialog"; +import { IconizedContextMenuOption } from "./IconizedContextMenu"; +import { _t } from "../../../languageHandler"; + +interface Props { + onFinished: () => void; + roomId: string; +} + +export const DeveloperToolsOption: React.FC = ({ onFinished, roomId }) => { + return ( + { + Modal.createDialog( + DevtoolsDialog, + { + onFinished: () => {}, + roomId: roomId, + }, + "mx_DevtoolsDialog_wrapper", + ); + onFinished(); + }} + label={_t("Developer tools")} + iconClassName="mx_IconizedContextMenu_developerTools" + /> + ); +}; diff --git a/src/components/views/context_menus/RoomContextMenu.tsx b/src/components/views/context_menus/RoomContextMenu.tsx index 5c2b0c3884..4be907c161 100644 --- a/src/components/views/context_menus/RoomContextMenu.tsx +++ b/src/components/views/context_menus/RoomContextMenu.tsx @@ -1,5 +1,5 @@ /* -Copyright 2021 The Matrix.org Foundation C.I.C. +Copyright 2021 - 2023 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -48,15 +48,18 @@ import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; import { getKeyBindingsManager } from "../../../KeyBindingsManager"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; import SettingsStore from "../../../settings/SettingsStore"; -import DevtoolsDialog from "../dialogs/DevtoolsDialog"; import { SdkContextClass } from "../../../contexts/SDKContext"; import { shouldShowComponent } from "../../../customisations/helpers/UIComponents"; import { UIComponent } from "../../../settings/UIFeature"; +import { DeveloperToolsOption } from "./DeveloperToolsOption"; interface IProps extends IContextMenuProps { room: Room; } +/** + * Room context menu accessible via the room header. + */ const RoomContextMenu: React.FC = ({ room, onFinished, ...props }) => { const cli = useContext(MatrixClientContext); const roomTags = useEventEmitterState(RoomListStore.instance, LISTS_UPDATE_EVENT, () => @@ -393,23 +396,7 @@ const RoomContextMenu: React.FC = ({ room, onFinished, ...props }) => { {exportChatOption} {SettingsStore.getValue("developerMode") && ( - { - ev.preventDefault(); - ev.stopPropagation(); - - Modal.createDialog( - DevtoolsDialog, - { - roomId: room.roomId, - }, - "mx_DevtoolsDialog_wrapper", - ); - onFinished(); - }} - label={_t("Developer tools")} - iconClassName="mx_RoomTile_iconDeveloperTools" - /> + )} {leaveOption} diff --git a/src/components/views/context_menus/RoomGeneralContextMenu.tsx b/src/components/views/context_menus/RoomGeneralContextMenu.tsx index 711b5fb910..901ed519b6 100644 --- a/src/components/views/context_menus/RoomGeneralContextMenu.tsx +++ b/src/components/views/context_menus/RoomGeneralContextMenu.tsx @@ -1,5 +1,5 @@ /* -Copyright 2021 The Matrix.org Foundation C.I.C. +Copyright 2021 - 2023 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -40,6 +40,8 @@ import IconizedContextMenu, { import { ButtonEvent } from "../elements/AccessibleButton"; import { shouldShowComponent } from "../../../customisations/helpers/UIComponents"; import { UIComponent } from "../../../settings/UIFeature"; +import { DeveloperToolsOption } from "./DeveloperToolsOption"; +import { useSettingValue } from "../../../hooks/useSettings"; export interface RoomGeneralContextMenuProps extends IContextMenuProps { room: Room; @@ -52,6 +54,9 @@ export interface RoomGeneralContextMenuProps extends IContextMenuProps { onPostLeaveClick?: (event: ButtonEvent) => void; } +/** + * Room context menu accessible via the room list. + */ export const RoomGeneralContextMenu: React.FC = ({ room, onFinished, @@ -221,6 +226,11 @@ export const RoomGeneralContextMenu: React.FC = ({ /> ) : null; + const developerModeEnabled = useSettingValue("developerMode"); + const developerToolsOption = developerModeEnabled ? ( + + ) : null; + return ( @@ -234,6 +244,7 @@ export const RoomGeneralContextMenu: React.FC = ({ {settingsOption} )} + {developerToolsOption} {leaveOption} diff --git a/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx b/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx index cb15fdfe1a..b5c1e80869 100644 --- a/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx @@ -1,5 +1,5 @@ /* -Copyright 2019 - 2022 The Matrix.org Foundation C.I.C. +Copyright 2019 - 2023 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/test/components/views/context_menus/RoomContextMenu-test.tsx b/test/components/views/context_menus/RoomContextMenu-test.tsx index 1d1dffce5f..a1e9f58b70 100644 --- a/test/components/views/context_menus/RoomContextMenu-test.tsx +++ b/test/components/views/context_menus/RoomContextMenu-test.tsx @@ -1,6 +1,7 @@ /* Copyright 2023 Mikhail Aheichyk Copyright 2023 Nordeck IT + Consulting GmbH. +Copyright 2023 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -27,6 +28,7 @@ import { shouldShowComponent } from "../../../../src/customisations/helpers/UICo import { stubClient } from "../../../test-utils"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import DMRoomMap from "../../../../src/utils/DMRoomMap"; +import SettingsStore from "../../../../src/settings/SettingsStore"; jest.mock("../../../../src/customisations/helpers/UIComponents", () => ({ shouldShowComponent: jest.fn(), @@ -58,8 +60,8 @@ describe("RoomContextMenu", () => { onFinished = jest.fn(); }); - function getComponent(props: Partial> = {}) { - return render( + function renderComponent(props: Partial> = {}) { + render( , @@ -70,7 +72,7 @@ describe("RoomContextMenu", () => { jest.spyOn(room, "canInvite").mockReturnValue(true); mocked(shouldShowComponent).mockReturnValue(false); - getComponent(); + renderComponent(); expect(screen.queryByRole("menuitem", { name: "Invite" })).not.toBeInTheDocument(); }); @@ -79,8 +81,24 @@ describe("RoomContextMenu", () => { jest.spyOn(room, "canInvite").mockReturnValue(true); mocked(shouldShowComponent).mockReturnValue(true); - getComponent(); + renderComponent(); expect(screen.getByRole("menuitem", { name: "Invite" })).toBeInTheDocument(); }); + + it("when developer mode is disabled, it should not render the developer tools option", () => { + renderComponent(); + expect(screen.queryByText("Developer tools")).not.toBeInTheDocument(); + }); + + describe("when developer mode is enabled", () => { + beforeEach(() => { + jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => setting === "developerMode"); + }); + + it("should render the developer tools option", () => { + renderComponent(); + expect(screen.getByText("Developer tools")).toBeInTheDocument(); + }); + }); }); diff --git a/test/components/views/context_menus/RoomGeneralContextMenu-test.tsx b/test/components/views/context_menus/RoomGeneralContextMenu-test.tsx index cadf067ef5..df04e1c054 100644 --- a/test/components/views/context_menus/RoomGeneralContextMenu-test.tsx +++ b/test/components/views/context_menus/RoomGeneralContextMenu-test.tsx @@ -1,5 +1,5 @@ /* -Copyright 2022 The Matrix.org Foundation C.I.C. +Copyright 2022 - 2023 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import { ReceiptType } from "matrix-js-sdk/src/@types/read_receipts"; import { MatrixClient, PendingEventOrdering } from "matrix-js-sdk/src/client"; import { Room } from "matrix-js-sdk/src/models/room"; import React from "react"; +import userEvent from "@testing-library/user-event"; import { ChevronFace } from "../../../../src/components/structures/ContextMenu"; import { @@ -34,6 +35,8 @@ import DMRoomMap from "../../../../src/utils/DMRoomMap"; import { mkMessage, stubClient } from "../../../test-utils/test-utils"; import { shouldShowComponent } from "../../../../src/customisations/helpers/UIComponents"; import { UIComponent } from "../../../../src/settings/UIFeature"; +import SettingsStore from "../../../../src/settings/SettingsStore"; +import Modal from "../../../../src/Modal"; jest.mock("../../../../src/customisations/helpers/UIComponents", () => ({ shouldShowComponent: jest.fn(), @@ -87,6 +90,10 @@ describe("RoomGeneralContextMenu", () => { onFinished = jest.fn(); }); + afterEach(() => { + Modal.closeCurrentModal("force"); + }); + it("renders an empty context menu for archived rooms", async () => { jest.spyOn(RoomListStore.instance, "getTagsForRoom").mockReturnValueOnce([DefaultTagID.Archived]); @@ -138,4 +145,28 @@ describe("RoomGeneralContextMenu", () => { expect(mockClient.sendReadReceipt).toHaveBeenCalledWith(event, ReceiptType.Read, true); expect(onFinished).toHaveBeenCalled(); }); + + it("when developer mode is disabled, it should not render the developer tools option", () => { + getComponent(); + expect(screen.queryByText("Developer tools")).not.toBeInTheDocument(); + }); + + describe("when developer mode is enabled", () => { + beforeEach(() => { + jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => setting === "developerMode"); + getComponent(); + }); + + it("should render the developer tools option", async () => { + const developerToolsItem = screen.getByRole("menuitem", { name: "Developer tools" }); + expect(developerToolsItem).toBeInTheDocument(); + + // click open developer tools dialog + await userEvent.click(developerToolsItem); + + // assert that the dialog is displayed by searching some if its contents + expect(await screen.findByText("Toolbox")).toBeInTheDocument(); + expect(await screen.findByText(`Room ID: ${ROOM_ID}`)).toBeInTheDocument(); + }); + }); });