From b22eba7af92a03fc1bd8bd59999fca7da9b7934e Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Fri, 4 Sep 2026 07:39:58 +0200 Subject: [PATCH] Add further tests --- .../localMember/LocalMember.test.ts | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/src/state/CallViewModel/localMember/LocalMember.test.ts b/src/state/CallViewModel/localMember/LocalMember.test.ts index 09d863aa2..b270db13e 100644 --- a/src/state/CallViewModel/localMember/LocalMember.test.ts +++ b/src/state/CallViewModel/localMember/LocalMember.test.ts @@ -31,6 +31,7 @@ import { flushPromises, mockConfig, mockLivekitRoom, + mockLocalParticipant, mockMuteStates, withTestScheduler, ownMemberMock, @@ -880,4 +881,99 @@ describe("LocalMembership", () => { scope.end(); }); }); + + describe("toggleScreenSharing", () => { + let originalMediaDevices: MediaDevices | undefined; + + beforeAll(() => { + mockConfig(); + // Screen sharing is only offered when getDisplayMedia is available. + originalMediaDevices = navigator.mediaDevices; + Object.defineProperty(navigator, "mediaDevices", { + configurable: true, + value: { getDisplayMedia: vi.fn() }, + }); + }); + + afterAll(() => { + Object.defineProperty(navigator, "mediaDevices", { + configurable: true, + value: originalMediaDevices, + }); + }); + + const createMembershipWithConnection = ( + connection: Connection | null, + ): { + scope: ObservableScope; + localMembership: ReturnType; + } => { + const scope = new ObservableScope(); + const connectionManagerData = new ConnectionManagerData(); + if (connection) connectionManagerData.add(connection, []); + const localMembership = createLocalMembership$({ + scope, + ...defaultCreateLocalMemberValues, + connectionManager: { + connectionManagerData$: constant(new Epoch(connectionManagerData)), + }, + localTransport$: new BehaviorSubject({ + advertised$: new BehaviorSubject(aTransport), + active$: new BehaviorSubject(aTransportWithSFUConfig), + }), + }); + return { scope, localMembership }; + }; + + it("surfaces a failure and clears it on dismiss", async () => { + const error = new Error("NotReadableError"); + const setScreenShareEnabled = vi.fn().mockRejectedValue(error); + const connection = { + state$: constant(ConnectionState.LivekitConnected), + transport: aTransport, + livekitRoom: mockLivekitRoom({ + localParticipant: mockLocalParticipant({ + isScreenShareEnabled: false, + setScreenShareEnabled, + }), + }), + } as unknown as Connection; + const { scope, localMembership } = + createMembershipWithConnection(connection); + await flushPromises(); + + expect(localMembership.toggleScreenSharing).not.toBeNull(); + expect(localMembership.screenShareError$.value).toBeNull(); + + localMembership.toggleScreenSharing!(); + await flushPromises(); + + expect(setScreenShareEnabled).toHaveBeenCalledWith( + true, + expect.any(Object), + undefined, + ); + expect(localMembership.screenShareError$.value).toBe(error); + + localMembership.dismissScreenShareError(); + expect(localMembership.screenShareError$.value).toBeNull(); + + scope.end(); + }); + + it("does nothing when there is no local participant", async () => { + // No connection means participant$ never resolves to a participant. + const { scope, localMembership } = + createMembershipWithConnection(null); + await flushPromises(); + + expect(localMembership.toggleScreenSharing).not.toBeNull(); + localMembership.toggleScreenSharing!(); + await flushPromises(); + + expect(localMembership.screenShareError$.value).toBeNull(); + + scope.end(); + }); + }); });