/* 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. */ import { renderHook, waitFor } from "jest-matrix-react"; import React from "react"; import { PlatformCallType, useRoomCall } from "../../../src/hooks/room/useRoomCall"; import { getMockClientWithEventEmitter, mkRoom, mockClientMethodsRooms, mockClientMethodsServer, mockClientMethodsUser, MockEventEmitter, setupAsyncStoreWithClient, } from "../../test-utils"; import { ScopedRoomContextProvider } from "../../../src/contexts/ScopedRoomContext"; import RoomContext, { type RoomContextType } from "../../../src/contexts/RoomContext"; import { MatrixClientContextProvider } from "../../../src/components/structures/MatrixClientContextProvider"; import type LegacyCallHandler from "../../../src/LegacyCallHandler"; import { SdkContextClass } from "../../../src/contexts/SDKContext"; import SettingsStore from "../../../src/settings/SettingsStore"; import { CallStore } from "../../../src/stores/CallStore"; describe("useRoomCall", () => { const client = getMockClientWithEventEmitter({ ...mockClientMethodsUser(), ...mockClientMethodsServer(), ...mockClientMethodsRooms(), matrixRTC: new MockEventEmitter(), _unstable_getRTCTransports: jest.fn().mockResolvedValue([]), getCrypto: () => null, }); const room = mkRoom(client, "!test-room"); // Create a stable room context for this test const mockRoomViewStore = { isViewingCall: jest.fn().mockReturnValue(false), on: jest.fn(), off: jest.fn(), emit: jest.fn(), }; const roomContext = { ...RoomContext, roomId: room.roomId, roomViewStore: mockRoomViewStore, } as unknown as RoomContextType; beforeEach(() => { const callHandler = { getCallForRoom: jest.fn().mockReturnValue(null), isCallSidebarShown: jest.fn().mockReturnValue(true), addListener: jest.fn(), removeListener: jest.fn(), on: jest.fn(), off: jest.fn(), }; jest.spyOn(SdkContextClass.instance, "legacyCallHandler", "get").mockReturnValue( callHandler as unknown as LegacyCallHandler, ); const origGetValue = SettingsStore.getValue; jest.spyOn(SettingsStore, "getValue").mockImplementation((name, ...params): any => { if (name === "feature_group_calls") return true; return origGetValue(name, ...params); }); }); afterEach(() => { jest.restoreAllMocks(); }); function render() { return renderHook(() => useRoomCall(room), { wrapper: ({ children }) => ( {children} ), }); } describe("Element Call focus detection", () => { it("Blocks Element Call if required foci are not configured", async () => { await setupAsyncStoreWithClient(CallStore.instance, client); const { result } = render(); await waitFor(() => expect(result.current.callOptions).toEqual([PlatformCallType.LegacyCall])); }); it("Blocks Element Call if transport foci are the wrong type", async () => { client._unstable_getRTCTransports.mockResolvedValue([{ type: "anything-else" }]); await setupAsyncStoreWithClient(CallStore.instance, client); const { result } = render(); await waitFor(() => expect(result.current.callOptions).toEqual([PlatformCallType.LegacyCall])); }); it("Blocks Element Call if well-known foci are the wrong type", async () => { client.getClientWellKnown.mockReturnValue({ "org.matrix.msc4143.rtc_foci": { type: "anything-else", }, }); await setupAsyncStoreWithClient(CallStore.instance, client); const { result } = render(); await waitFor(() => expect(result.current.callOptions).toEqual([PlatformCallType.LegacyCall])); }); it("Allows Element Call if foci is provided via getRTCTransports", async () => { client._unstable_getRTCTransports.mockResolvedValue([ { type: "livekit", livekit_service_url: "https://example.org" }, ]); await setupAsyncStoreWithClient(CallStore.instance, client); const { result } = render(); await waitFor(() => expect(result.current.callOptions).toEqual([PlatformCallType.ElementCall, PlatformCallType.LegacyCall]), ); }); it("Allows Element Call if foci is provided via .well-known", async () => { client.getClientWellKnown.mockReturnValue({ "org.matrix.msc4143.rtc_foci": { type: "livekit", livekit_service_url: "https://example.org", }, }); await setupAsyncStoreWithClient(CallStore.instance, client); const { result } = render(); await waitFor(() => expect(result.current.callOptions).toEqual([PlatformCallType.ElementCall, PlatformCallType.LegacyCall]), ); }); it("Ensure handler reacts to transport changes", async () => { // Clear all transports client._unstable_getRTCTransports.mockResolvedValue([]); client.getClientWellKnown.mockReturnValue({}); await setupAsyncStoreWithClient(CallStore.instance, client); const { result } = render(); // Ensure Element Call is not a call option. expect(result.current.callOptions).toEqual([PlatformCallType.LegacyCall]); // Now enable a transport and ensure that useRoomCall picks it up reactively. client._unstable_getRTCTransports.mockResolvedValue([ { type: "livekit", livekit_service_url: "https://example.org" }, ]); await setupAsyncStoreWithClient(CallStore.instance, client); await waitFor(() => expect(result.current.callOptions).toEqual([PlatformCallType.ElementCall, PlatformCallType.LegacyCall]), ); }); }); });