Files
element-call/src/lotus/LotusFrameScreenshare.test.tsx
T
Lotus CIandClaude Opus 5.5 0f93368a7e feat(lotus): in-frame screenshare button where the host can't delegate (cinny #43)
Firefox, Safari and WebKitGTK have no Capability Delegation, so the host
can't start a share from its call bar: getDisplayMedia needs the click in
this frame. Until now the host clicked EC's hidden footer button through
the DOM, which only works while the frame is same-origin.

With lotusHostControls + lotusFrameScreenshare the fork now shows EC's own
screenshare button (floating bottom-right, footer still hidden), so the
click is native. controls_state reports frameScreenshare so the host hides
its own button. New toWidget io.lotus.set_frame_screenshare { visible }
lets the host hide it while the room's call policy forbids screensharing
(never while a share is live, so it can still be stopped).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-26 21:48:22 -04:00

89 lines
3.0 KiB
TypeScript

/*
Copyright 2026 Lotus Guild
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details.
*/
import { describe, expect, it, vi } from "vitest";
import { act, render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { TooltipProvider } from "@vector-im/compound-web";
import { BehaviorSubject } from "rxjs";
import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
import { LotusFrameScreenshare } from "./LotusFrameScreenshare";
import {
lotusFrameScreenshareEnabled,
lotusFrameScreenshareVisible$,
parseFrameScreensharePayload,
} from "./lotusControls";
// lotusFlag memoizes the URL params on first read, so set them once here.
window.history.pushState(
{},
"",
"/?lotusHostControls=1&lotusFrameScreenshare=1",
);
const mockVm = (
toggle: (() => void) | null,
sharing = new BehaviorSubject(false),
): CallViewModel =>
({
sharingScreen$: sharing,
toggleScreenSharing: toggle,
}) as unknown as CallViewModel;
const renderButton = (vm: CallViewModel): void => {
render(
<TooltipProvider>
<LotusFrameScreenshare vm={vm} />
</TooltipProvider>,
);
};
describe("LotusFrameScreenshare", () => {
it("is enabled by lotusHostControls + lotusFrameScreenshare", () => {
expect(lotusFrameScreenshareEnabled()).toBe(true);
});
it("toggles the share from a click inside the frame", async () => {
const toggle = vi.fn();
renderButton(mockVm(toggle));
await userEvent.click(screen.getByTestId("lotus_frame_screenshare"));
expect(toggle).toHaveBeenCalledTimes(1);
});
it("follows the sharing state", () => {
const sharing = new BehaviorSubject(false);
renderButton(mockVm(vi.fn(), sharing));
const button = screen.getByTestId("lotus_frame_screenshare");
expect(button).toHaveAttribute("aria-checked", "false");
act(() => sharing.next(true));
expect(button).toHaveAttribute("aria-checked", "true");
});
it("hides while the host's policy forbids sharing, unless a share is live", () => {
const sharing = new BehaviorSubject(false);
act(() => lotusFrameScreenshareVisible$.next(false));
renderButton(mockVm(vi.fn(), sharing));
expect(screen.queryByTestId("lotus_frame_screenshare")).toBeNull();
act(() => sharing.next(true));
expect(screen.getByTestId("lotus_frame_screenshare")).toBeInTheDocument();
act(() => lotusFrameScreenshareVisible$.next(true));
});
it("parses the set_frame_screenshare payload", () => {
expect(parseFrameScreensharePayload({ visible: false })).toBe(false);
expect(parseFrameScreensharePayload({ visible: true })).toBe(true);
expect(parseFrameScreensharePayload({})).toBeUndefined();
expect(parseFrameScreensharePayload(null)).toBeUndefined();
});
it("renders nothing when screensharing is unavailable", () => {
renderButton(mockVm(null));
expect(screen.queryByTestId("lotus_frame_screenshare")).toBeNull();
});
});