From 25bd76d7368f69796363001be98bb3c26b469032 Mon Sep 17 00:00:00 2001 From: "marc.sirisak" Date: Wed, 9 Oct 2024 12:48:51 +0200 Subject: [PATCH 1/4] feat(group_call): add new tchap video group call config, differenciate from 1to1 video call --- README_tchap.md | 13 +++++++++++++ config.dev.json | 19 ++++++------------- config.preprod.json | 1 + config.prod.json | 1 + config.prod.lab.json | 1 + .../src/components/views/rooms/RoomHeader.tsx | 3 +++ 6 files changed, 25 insertions(+), 13 deletions(-) diff --git a/README_tchap.md b/README_tchap.md index 24a3e7aba..620a499b6 100644 --- a/README_tchap.md +++ b/README_tchap.md @@ -1,3 +1,16 @@ +## Config variable + +- tchap_features : Object containing the feature that can be activated by homeserver + - "feature_email_notification": Email notification + - "feature_space": Creation of spaces + - "feature_thread": Activate thread on messages + - "feature_audio_call": Activate 1 to 1 voice call + - "feature_video_call": Activate 1 to 1 video call + - "feature_video_group_call": Activate group call on rooms, for this feature to work, the values of `UIFeature.widgets` and `feature_group_calls` needs to be true + - "feature_screenshare_call": Activate 1 to 1 screenshare +- "tchap_sso_flow" + - "isActive": Activate ProConnect SSO flow + ## File structures - modules -> used for translation diff --git a/config.dev.json b/config.dev.json index 73627a934..a0d8b3a06 100644 --- a/config.dev.json +++ b/config.dev.json @@ -32,7 +32,7 @@ "default_country_code": "FR", "show_labs_settings": false, "features": { - "feature_video_rooms": true, + "feature_video_rooms": false, "feature_notification_settings2": false, "feature_new_room_decoration_ui": true, "feature_rust_crypto": true, @@ -58,7 +58,7 @@ "UIFeature.shareSocial": false, "UIFeature.registration": true, "UIFeature.urlPreviews": false, - "UIFeature.widgets": false, + "UIFeature.widgets": true, "UIFeature.shareQrCode": false, "UIFeature.thirdPartyId": true, "UIFeature.identityServer": true, @@ -112,17 +112,10 @@ "tchap_features": { "feature_email_notification": ["*"], "feature_space": ["*"], - "feature_thread": ["dev01.tchap.incubateur.net", "dev02.tchap.incubateur.net", "ext01.tchap.incubateur.net"], - "feature_audio_call": [ - "dev01.tchap.incubateur.net", - "dev02.tchap.incubateur.net", - "ext01.tchap.incubateur.net" - ], - "feature_video_call": [ - "dev01.tchap.incubateur.net", - "dev02.tchap.incubateur.net", - "ext01.tchap.incubateur.net" - ], + "feature_thread": ["*"], + "feature_audio_call": ["*"], + "feature_video_call": ["*"], + "feature_video_group_call": ["*"], "feature_screenshare_call": ["*"] }, "tchap_sso_flow": { diff --git a/config.preprod.json b/config.preprod.json index 5864003de..29c19453e 100644 --- a/config.preprod.json +++ b/config.preprod.json @@ -109,6 +109,7 @@ "feature_space": ["*"], "feature_audio_call": ["i.tchap.gouv.fr", "e.tchap.gouv.fr"], "feature_video_call": ["i.tchap.gouv.fr", "e.tchap.gouv.fr"], + "feature_video_group_call": [], "feature_screenshare_call": ["*"] }, "tchap_sso_flow": { diff --git a/config.prod.json b/config.prod.json index a7192ce50..47219c1bc 100644 --- a/config.prod.json +++ b/config.prod.json @@ -196,6 +196,7 @@ "feature_space": ["*"], "feature_audio_call": ["*"], "feature_video_call": ["agent.dinum.tchap.gouv.fr"], + "feature_video_group_call": [], "feature_screenshare_call": ["*"] }, "tchap_sso_flow": { diff --git a/config.prod.lab.json b/config.prod.lab.json index 33fa0bdce..ff571e85a 100644 --- a/config.prod.lab.json +++ b/config.prod.lab.json @@ -196,6 +196,7 @@ "feature_space": ["agent.dinum.tchap.gouv.fr"], "feature_audio_call": ["*"], "feature_video_call": ["agent.dinum.tchap.gouv.fr", "education.tchap.gouv.fr"], + "feature_video_group_call": [], "feature_screenshare_call": ["*"] }, "tchap_sso_flow": { diff --git a/linked-dependencies/matrix-react-sdk/src/components/views/rooms/RoomHeader.tsx b/linked-dependencies/matrix-react-sdk/src/components/views/rooms/RoomHeader.tsx index be14a0409..cb486aa18 100644 --- a/linked-dependencies/matrix-react-sdk/src/components/views/rooms/RoomHeader.tsx +++ b/linked-dependencies/matrix-react-sdk/src/components/views/rooms/RoomHeader.tsx @@ -349,6 +349,9 @@ export default function RoomHeader({ { /* :TCHAP: customize-room-header-bar - activate video call only if directmessage and if feature is activated on homeserver } {!isVideoRoom && videoCallButton} */ } + {!isDirectMessage && TchapUIFeature.isFeatureActiveForHomeserver("feature_video_group_call") && + !isVideoRoom && videoCallButton} + {isDirectMessage && TchapUIFeature.isFeatureActiveForHomeserver("feature_video_call") && !isVideoRoom && videoCallButton} {/* end :TCHAP: */} From 3092fae3bfa40a6eff1c763644edbcaaf1cf50bf Mon Sep 17 00:00:00 2001 From: "marc.sirisak" Date: Thu, 10 Oct 2024 15:35:35 +0200 Subject: [PATCH 2/4] test(roomheader): add video call group and 1v1 tests --- .../views/rooms/RoomHeaders-test.tsx | 120 ++++++++++++++++-- 1 file changed, 111 insertions(+), 9 deletions(-) diff --git a/test/unit-tests/tchap/components/views/rooms/RoomHeaders-test.tsx b/test/unit-tests/tchap/components/views/rooms/RoomHeaders-test.tsx index 942e14933..47307a904 100644 --- a/test/unit-tests/tchap/components/views/rooms/RoomHeaders-test.tsx +++ b/test/unit-tests/tchap/components/views/rooms/RoomHeaders-test.tsx @@ -1,13 +1,15 @@ import React from "react"; -import { PendingEventOrdering, Room } from "matrix-js-sdk/src/matrix"; -import { screen, render, RenderOptions } from "@testing-library/react"; +import { KnownMembership, PendingEventOrdering, Room } from "matrix-js-sdk/src/matrix"; +import { screen, render, RenderOptions, getByLabelText, queryByLabelText } from "@testing-library/react"; -import { stubClient } from "~matrix-react-sdk/test/test-utils"; +import { mkRoomMember, stubClient } from "~matrix-react-sdk/test/test-utils"; import RoomHeader from "~matrix-react-sdk/src/components/views/rooms/RoomHeader"; import DMRoomMap from "~matrix-react-sdk/src/utils/DMRoomMap"; import { MatrixClientPeg } from "~matrix-react-sdk/src/MatrixClientPeg"; import MatrixClientContext from "~matrix-react-sdk/src/contexts/MatrixClientContext"; import SdkConfig from "~matrix-react-sdk/src/SdkConfig"; +import SettingsStore from "~matrix-react-sdk/src/settings/SettingsStore"; +import { UIFeature } from "~matrix-react-sdk/src/settings/UIFeature"; function getWrapper(): RenderOptions { return { @@ -17,10 +19,33 @@ function getWrapper(): RenderOptions { }; } +/** + * + * @param count the number of users to create + */ +function mockRoomMembers(room: Room, count: number) { + const members = Array(count) + .fill(0) + .map((_, index) => ({ + userId: `@user-${index}:example.org`, + name: `Member ${index}`, + rawDisplayName: `Member ${index}`, + roomId: room.roomId, + membership: KnownMembership.Join, + getAvatarUrl: () => `mxc://avatar.url/user-${index}.png`, + getMxcAvatarUrl: () => `mxc://avatar.url/user-${index}.png`, + })); + + room.currentState.setJoinedMemberCount(members.length); + room.getJoinedMembers = jest.fn().mockReturnValue(members); +} + describe("RoomHeader", () => { let room: Room; const ROOM_ID = "!1:example.org"; const featurethreadName: string = "feature_thread"; + const featureVideoName: string = "feature_video_call"; + const featureVideoGroupName: string = "feature_video_group_call"; const homeserverName: string = "my.home.server"; const addHomeserverToMockConfig = (homeservers: string[], feature: string) => { @@ -32,11 +57,38 @@ describe("RoomHeader", () => { const getComponent = () => render(, getWrapper()); + function mockDMRoom(memberCount: number = 2) { + mockRoomMembers(room, memberCount); + jest.spyOn(SettingsStore, "getValue").mockImplementation(() => false); + jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(false); + jest.spyOn(room, "getMember").mockReturnValue(mkRoomMember(room.roomId, "@alice:example.org")); + + DMRoomMap.setShared({ + getUserIdForRoomId: () => { + return "@alice:example.org"; + }, + } as unknown as DMRoomMap); + } + beforeEach(async () => { - stubClient(); + const mockClient = stubClient(); room = new Room(ROOM_ID, MatrixClientPeg.get()!, "@alice:example.org", { pendingEventOrdering: PendingEventOrdering.Detached, }); + + jest.spyOn(mockClient, "getDomain").mockImplementation(() => homeserverName); + jest.spyOn(room, "isElementVideoRoom").mockReturnValue(false); + jest.spyOn(room, "isCallRoom").mockReturnValue(false); + jest.spyOn(room, "isSpaceRoom").mockReturnValue(false); + jest.spyOn(room, "getType").mockReturnValue(undefined); + + // allow element calls + jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true); + // activate the group and widget features + jest.spyOn(SettingsStore, "getValue").mockImplementation( + (feature) => feature === "feature_group_calls" || feature == UIFeature.Widgets, + ); + DMRoomMap.setShared({ getUserIdForRoomId: jest.fn(), } as unknown as DMRoomMap); @@ -52,19 +104,69 @@ describe("RoomHeader", () => { expect(container).toHaveTextContent(ROOM_ID); }); - it("display well the thread button when feature is activated", async () => { - addHomeserverToMockConfig([homeserverName], featurethreadName); + it("display well the thread button when feature is activated", () => { + addHomeserverToMockConfig(["*"], featurethreadName); getComponent(); - expect(screen.queryByTestId("room-header-thread-button")).toBeInTheDocument; + expect(screen.queryByRole("button", { name: "Threads" })).toBeInTheDocument(); }); - it("hides the thread button when feature is deactivated", async () => { + it("hides the thread button when feature is deactivated", () => { addHomeserverToMockConfig(["other.homeserver"], featurethreadName); getComponent(); - expect(screen.queryByTestId("room-header-thread-button")).not.toBeInTheDocument; + expect(screen.queryByRole("button", { name: "Threads" })).not.toBeInTheDocument(); + }); + + // For 1 to 1 video call + it("display well the video button when feature is activated for 1v1 call", () => { + addHomeserverToMockConfig([homeserverName], featureVideoName); + mockDMRoom(); + + const { container } = getComponent(); + + expect(queryByLabelText(container, "Video call")).toBeInTheDocument(); + }); + + it("hides the video button when feature is deactivated for 1v1 call", () => { + addHomeserverToMockConfig(["other.homeserver"], featureVideoName); + mockDMRoom(); + + const { container } = getComponent(); + + expect(queryByLabelText(container, "Video call")).toBeNull(); + }); + + it("hides the video button when feature is activated but is not a direct message room", () => { + addHomeserverToMockConfig([homeserverName], featureVideoName); + + mockDMRoom(4); + + const { container } = getComponent(); + + expect(queryByLabelText(container, "Video call")).toBeNull(); + }); + + // for video group element call button + it("display well the video group button when feature is activated", () => { + addHomeserverToMockConfig([homeserverName], featureVideoGroupName); + + mockRoomMembers(room, 4); + + const { container } = getComponent(); + + expect(getByLabelText(container, "Video call")).toBeInTheDocument(); + }); + + it("hides the video group when feature is deactivated", () => { + addHomeserverToMockConfig(["other.homeserver"], featureVideoGroupName); + + mockRoomMembers(room, 4); + + const { container } = getComponent(); + + expect(queryByLabelText(container, "Video call")).toBeNull(); }); }); From 6331851f4f1406d73fd7d7a8506334384fbbf6ae Mon Sep 17 00:00:00 2001 From: "marc.sirisak" Date: Thu, 10 Oct 2024 15:35:58 +0200 Subject: [PATCH 3/4] test(setup): make translation work in tests --- test/setup/setupLanguage.ts | 2 +- test/setupTests.ts | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/test/setup/setupLanguage.ts b/test/setup/setupLanguage.ts index a90578c2c..f0a435623 100644 --- a/test/setup/setupLanguage.ts +++ b/test/setup/setupLanguage.ts @@ -9,9 +9,9 @@ Please see LICENSE files in the repository root for full details. import fetchMock from "fetch-mock-jest"; import _ from "lodash"; import { setupLanguageMock as reactSetupLanguageMock } from "matrix-react-sdk/test/setup/setupLanguage"; +import reactEn from "matrix-react-sdk/src/i18n/strings/en_EN.json"; // :TCHAP: we want to have the sdk translation and element since we might have test for both repo import en from "../../src/i18n/strings/en_EN.json"; -import reactEn from "../../src/i18n/strings/en_EN.json"; fetchMock.config.overwriteRoutes = false; diff --git a/test/setupTests.ts b/test/setupTests.ts index d0de870dc..6ae9415ee 100644 --- a/test/setupTests.ts +++ b/test/setupTests.ts @@ -1 +1,11 @@ import "@testing-library/jest-dom"; +// :TCHAP: +// Very carefully enable the mocks for everything else in +// a specific order. We use this order to ensure we properly +// establish an application state that actually works. +// +// These are also require() calls to make sure they get called +// synchronously. +require("matrix-react-sdk/test/setup/setupManualMocks"); // must be first +require("./setup/setupLanguage"); +require("matrix-react-sdk/test/setup/setupConfig"); From 8b70d6313b76d6af104fa02d69d47c9f5ac272d9 Mon Sep 17 00:00:00 2001 From: "marc.sirisak" Date: Thu, 10 Oct 2024 16:50:16 +0200 Subject: [PATCH 4/4] test(fix): fix some random tests and add snapshot --- .../components/structures/UserMenu-test.tsx | 6 +- .../__snapshots__/UserMenu-test.tsx.snap | 22 ++--- .../BugReportDialog-test.tsx.snap | 32 +++---- .../views/rooms/RoomHeaders-test.tsx | 5 + .../__snapshots__/RoomHeaders-test.tsx.snap | 91 ++++++++++++++++++ .../views/spaces/SpacePanel-test.tsx | 20 +++- .../__snapshots__/SpacePanel-test.tsx.snap | 92 +++++++++++++++++++ 7 files changed, 234 insertions(+), 34 deletions(-) create mode 100644 test/unit-tests/tchap/components/views/rooms/__snapshots__/RoomHeaders-test.tsx.snap create mode 100644 test/unit-tests/tchap/components/views/spaces/__snapshots__/SpacePanel-test.tsx.snap diff --git a/test/unit-tests/tchap/components/structures/UserMenu-test.tsx b/test/unit-tests/tchap/components/structures/UserMenu-test.tsx index 932da78e5..ec6044b5e 100644 --- a/test/unit-tests/tchap/components/structures/UserMenu-test.tsx +++ b/test/unit-tests/tchap/components/structures/UserMenu-test.tsx @@ -26,7 +26,7 @@ describe("", () => { // it"s a good indicator to see if this could introduce some regression on our code it("should render as expected", async () => { // open the user menu - screen.getByRole("button", { name: "a11y" }).click(); + screen.getByRole("button", { name: "User menu" }).click(); const menu = screen.getByRole("menu"); expect(menu).toMatchSnapshot(); @@ -46,9 +46,9 @@ describe("", () => { it("should open the faq when clicking on the faq button", () => { global.open = jest.fn(); // open the user menu - screen.getByRole("button", { name: "a11y" }).click(); + screen.getByRole("button", { name: "User menu" }).click(); // click on the faq - screen.getByRole("menuitem", { name: "common" }).click(); + screen.getByRole("menuitem", { name: "Help" }).click(); expect(global.open).toHaveBeenCalledTimes(1); expect(global.open).toHaveBeenCalledWith("https://www.tchap.gouv.fr/faq", "_blank"); }); diff --git a/test/unit-tests/tchap/components/structures/__snapshots__/UserMenu-test.tsx.snap b/test/unit-tests/tchap/components/structures/__snapshots__/UserMenu-test.tsx.snap index 3ffbfca49..e16b8817e 100644 --- a/test/unit-tests/tchap/components/structures/__snapshots__/UserMenu-test.tsx.snap +++ b/test/unit-tests/tchap/components/structures/__snapshots__/UserMenu-test.tsx.snap @@ -25,7 +25,7 @@ exports[` UI should render as expected 1`] = `
UI should render as expected 1`] = ` class="mx_IconizedContextMenu_optionList mx_IconizedContextMenu_optionList_notFirst" >
diff --git a/test/unit-tests/tchap/components/views/dialogs/__snapshots__/BugReportDialog-test.tsx.snap b/test/unit-tests/tchap/components/views/dialogs/__snapshots__/BugReportDialog-test.tsx.snap index bda6b9a3c..32f0ba03a 100644 --- a/test/unit-tests/tchap/components/views/dialogs/__snapshots__/BugReportDialog-test.tsx.snap +++ b/test/unit-tests/tchap/components/views/dialogs/__snapshots__/BugReportDialog-test.tsx.snap @@ -33,19 +33,19 @@ exports[` should render as expected 1`] = ` >

- bug_reporting + Reminder: Your browser is unsupported, so your experience may be unpredictable.

- bug_reporting + Debug logs contain application usage data including your username, the IDs or aliases of the rooms you have visited, which UI elements you last interacted with, and the usernames of other users. They do not contain messages.