mirror of
https://github.com/vector-im/element-call.git
synced 2026-10-10 23:25:52 +00:00
113 lines
3.6 KiB
TypeScript
113 lines
3.6 KiB
TypeScript
/*
|
|
Copyright 2026 Element Creations Ltd.
|
|
|
|
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 } from "vitest";
|
|
|
|
import { chooseSpotlightSpeaker, computeSpotlight } from "./layoutMedia";
|
|
import { type LocalUserMediaViewModel } from "./media/LocalUserMediaViewModel";
|
|
import { type RemoteUserMediaViewModel } from "./media/RemoteUserMediaViewModel";
|
|
import { type RingingMediaViewModel } from "./media/RingingMediaViewModel";
|
|
import { type ScreenShareViewModel } from "./media/ScreenShareViewModel";
|
|
|
|
// The layout functions only look at `type` and `local`, so stubs suffice
|
|
const local = { type: "user", local: true } as LocalUserMediaViewModel;
|
|
const alice = { type: "user", local: false } as RemoteUserMediaViewModel;
|
|
const bob = { type: "user", local: false } as RemoteUserMediaViewModel;
|
|
const carol = { type: "user", local: false } as RemoteUserMediaViewModel;
|
|
const screenShare = {
|
|
type: "screen share",
|
|
local: false,
|
|
} as ScreenShareViewModel;
|
|
const ringing = { type: "ringing" } as RingingMediaViewModel;
|
|
|
|
describe("chooseSpotlightSpeaker", () => {
|
|
const items = (
|
|
...speaking: boolean[]
|
|
): {
|
|
media: RemoteUserMediaViewModel | LocalUserMediaViewModel;
|
|
speaking: boolean;
|
|
}[] =>
|
|
[local, alice, bob].map((media, i) => ({
|
|
media,
|
|
speaking: speaking[i] ?? false,
|
|
}));
|
|
|
|
it("prefers a remote speaker over the local user", () => {
|
|
expect(chooseSpotlightSpeaker(undefined, items(true, false, true))).toBe(
|
|
bob,
|
|
);
|
|
});
|
|
|
|
it("falls back to any remote user, then the local user", () => {
|
|
expect(chooseSpotlightSpeaker(undefined, items())).toBe(alice);
|
|
expect(
|
|
chooseSpotlightSpeaker(undefined, [{ media: local, speaking: false }]),
|
|
).toBe(local);
|
|
expect(chooseSpotlightSpeaker(undefined, [])).toBeUndefined();
|
|
});
|
|
|
|
it("sticks with the previous speaker while they keep speaking", () => {
|
|
expect(chooseSpotlightSpeaker(bob, items(false, true, true))).toBe(bob);
|
|
});
|
|
|
|
it("sticks with the previous speaker when nobody is speaking", () => {
|
|
expect(chooseSpotlightSpeaker(bob, items())).toBe(bob);
|
|
});
|
|
|
|
it("switches when someone else speaks and the previous speaker is silent", () => {
|
|
expect(chooseSpotlightSpeaker(bob, items(false, true, false))).toBe(alice);
|
|
});
|
|
|
|
it("forgets a previous speaker who left, and never sticks to the local user", () => {
|
|
expect(chooseSpotlightSpeaker(carol, items())).toBe(alice);
|
|
expect(chooseSpotlightSpeaker(local, items())).toBe(alice);
|
|
});
|
|
});
|
|
|
|
describe("computeSpotlight", () => {
|
|
it("spotlights ringing media with the local user in the PiP", () => {
|
|
expect(
|
|
computeSpotlight({
|
|
ringing,
|
|
screenShares: [screenShare],
|
|
speaker: alice,
|
|
localPip: local,
|
|
}),
|
|
).toEqual({ spotlight: [ringing], pip: local });
|
|
});
|
|
|
|
it("spotlights screen shares with the speaker in the PiP", () => {
|
|
expect(
|
|
computeSpotlight({
|
|
ringing: null,
|
|
screenShares: [screenShare],
|
|
speaker: alice,
|
|
localPip: local,
|
|
}),
|
|
).toEqual({ spotlight: [screenShare], pip: alice });
|
|
});
|
|
|
|
it("otherwise spotlights the speaker and hides a redundant PiP", () => {
|
|
expect(
|
|
computeSpotlight({
|
|
ringing: null,
|
|
screenShares: [],
|
|
speaker: alice,
|
|
localPip: local,
|
|
}),
|
|
).toEqual({ spotlight: [alice], pip: local });
|
|
expect(
|
|
computeSpotlight({
|
|
ringing: null,
|
|
screenShares: [],
|
|
speaker: local,
|
|
localPip: local,
|
|
}),
|
|
).toEqual({ spotlight: [local], pip: undefined });
|
|
});
|
|
});
|