/* Copyright 2025 New Vector Ltd. SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ import { beforeEach, describe, expect, it, type MockedFunction, vi, } from "vitest"; import { act, render, type RenderResult } from "@testing-library/react"; import { type MatrixClient, JoinRule, type RoomState } from "matrix-js-sdk"; import { type RelationsContainer } from "matrix-js-sdk/lib/models/relations-container"; import { type LocalParticipant } from "livekit-client"; import { of } from "rxjs"; import { BrowserRouter } from "react-router-dom"; import { TooltipProvider } from "@vector-im/compound-web"; import { RoomContext, useLocalParticipant } from "@livekit/components-react"; import { RoomAndToDeviceEvents } from "matrix-js-sdk/lib/matrixrtc/RoomAndToDeviceKeyTransport"; import { InCallView } from "./InCallView"; import { mockLivekitRoom, mockLocalParticipant, mockMatrixRoom, mockMatrixRoomMember, mockMediaDevices, mockMuteStates, mockRemoteParticipant, mockRtcMembership, type MockRTCSession, } from "../utils/test"; import { E2eeType } from "../e2ee/e2eeType"; import { getBasicCallViewModelEnvironment } from "../utils/test-viewmodel"; import { alice, local } from "../utils/test-fixtures"; import { developerMode as developerModeSetting, useExperimentalToDeviceTransport as useExperimentalToDeviceTransportSetting, } from "../settings/settings"; import { ReactionsSenderProvider } from "../reactions/useReactionsSender"; import { useRoomEncryptionSystem } from "../e2ee/sharedKeyManagement"; import { LivekitRoomAudioRenderer } from "../livekit/MatrixAudioRenderer"; import { MediaDevicesContext } from "../MediaDevicesContext"; import { HeaderStyle } from "../UrlParams"; // vi.hoisted(() => { // localStorage = {} as unknown as Storage; // }); vi.hoisted( () => (global.ImageData = class MockImageData { public data: number[] = []; } as unknown as typeof ImageData), ); vi.mock("../soundUtils"); vi.mock("../useAudioContext"); vi.mock("../tile/GridTile"); vi.mock("../tile/SpotlightTile"); vi.mock("@livekit/components-react"); vi.mock("../e2ee/sharedKeyManagement"); vi.mock("../livekit/MatrixAudioRenderer"); vi.mock("react-use-measure", () => ({ default: (): [() => void, object] => [(): void => {}, {}], })); const localRtcMember = mockRtcMembership("@carol:example.org", "CCCC"); const localParticipant = mockLocalParticipant({ identity: "@local:example.org:AAAAAA", }); const remoteParticipant = mockRemoteParticipant({ identity: "@alice:example.org:AAAAAA", }); const carol = mockMatrixRoomMember(localRtcMember); const roomMembers = new Map([carol].map((p) => [p.userId, p])); const roomId = "!foo:bar"; let useRoomEncryptionSystemMock: MockedFunction; beforeEach(() => { vi.clearAllMocks(); // MatrixAudioRenderer is tested separately. ( LivekitRoomAudioRenderer as MockedFunction ).mockImplementation((_props) => { return
mocked: MatrixAudioRenderer
; }); ( useLocalParticipant as MockedFunction ).mockImplementation( () => ({ isScreenShareEnabled: false, localParticipant: localRtcMember as unknown as LocalParticipant, }) as unknown as ReturnType, ); useRoomEncryptionSystemMock = useRoomEncryptionSystem as typeof useRoomEncryptionSystemMock; useRoomEncryptionSystemMock.mockReturnValue({ kind: E2eeType.NONE }); }); function createInCallView(): RenderResult & { rtcSession: MockRTCSession; } { const client = { getUser: () => null, getUserId: () => localRtcMember.sender, getDeviceId: () => localRtcMember.deviceId, getRoom: (rId) => (rId === roomId ? room : null), } as Partial as MatrixClient; const room = mockMatrixRoom({ relations: { getChildEventsForEvent: () => vi.mocked({ getRelations: () => [], }), } as unknown as RelationsContainer, client, roomId, getMember: (userId) => roomMembers.get(userId) ?? null, getMxcAvatarUrl: () => null, hasEncryptionStateEvent: vi.fn().mockReturnValue(true), getCanonicalAlias: () => null, currentState: { getJoinRule: () => JoinRule.Invite, } as Partial as RoomState, }); const muteState = mockMuteStates(); const livekitRoom = mockLivekitRoom( { localParticipant, }, { remoteParticipants$: of([remoteParticipant]), }, ); const { vm, rtcSession } = getBasicCallViewModelEnvironment([local, alice]); rtcSession.joined = true; const renderResult = render( , ); return { ...renderResult, rtcSession, }; } describe("InCallView", () => { describe("rendering", () => { it("renders", () => { const { container } = createInCallView(); expect(container).toMatchSnapshot(); }); }); describe("toDevice label", () => { it("is shown if setting activated and room encrypted", () => { useRoomEncryptionSystemMock.mockReturnValue({ kind: E2eeType.PER_PARTICIPANT, }); useExperimentalToDeviceTransportSetting.setValue(true); developerModeSetting.setValue(true); const { getByText } = createInCallView(); expect(getByText("using to Device key transport")).toBeInTheDocument(); }); it("is not shown in unenecrypted room", () => { useRoomEncryptionSystemMock.mockReturnValue({ kind: E2eeType.NONE, }); useExperimentalToDeviceTransportSetting.setValue(true); developerModeSetting.setValue(true); const { queryByText } = createInCallView(); expect( queryByText("using to Device key transport"), ).not.toBeInTheDocument(); }); it("is hidden once fallback was triggered", async () => { useRoomEncryptionSystemMock.mockReturnValue({ kind: E2eeType.PER_PARTICIPANT, }); useExperimentalToDeviceTransportSetting.setValue(true); developerModeSetting.setValue(true); const { rtcSession, queryByText } = createInCallView(); expect(queryByText("using to Device key transport")).toBeInTheDocument(); expect(rtcSession).toBeDefined(); await act(() => rtcSession.emit(RoomAndToDeviceEvents.EnabledTransportsChanged, { toDevice: true, room: true, }), ); expect( queryByText("using to Device key transport"), ).not.toBeInTheDocument(); }); it("is not shown if setting is disabled", () => { useExperimentalToDeviceTransportSetting.setValue(false); developerModeSetting.setValue(true); useRoomEncryptionSystemMock.mockReturnValue({ kind: E2eeType.PER_PARTICIPANT, }); const { queryByText } = createInCallView(); expect( queryByText("using to Device key transport"), ).not.toBeInTheDocument(); }); it("is not shown if developer mode is disabled", () => { useExperimentalToDeviceTransportSetting.setValue(true); developerModeSetting.setValue(false); useRoomEncryptionSystemMock.mockReturnValue({ kind: E2eeType.PER_PARTICIPANT, }); const { queryByText } = createInCallView(); expect( queryByText("using to Device key transport"), ).not.toBeInTheDocument(); }); }); });