more tests

This commit is contained in:
Timo K.
2026-09-09 17:01:16 +02:00
parent 59ef5f5400
commit be5f3390f7
4 changed files with 249 additions and 3 deletions
+27 -1
View File
@@ -36,6 +36,8 @@ import userEvent, {
import { type RelationsContainer } from "matrix-js-sdk/lib/models/relations-container";
import { useState } from "react";
import { TooltipProvider } from "@vector-im/compound-web";
import { Subject } from "rxjs";
import { Room as LivekitRoom } from "livekit-client";
import { prefetchSounds } from "../soundUtils";
import { useAudioContext } from "../useAudioContext";
@@ -54,8 +56,10 @@ import { GroupCallErrorBoundary } from "./GroupCallErrorBoundary";
import {
type HostBridge,
HostBridgeProvider,
type HostRequest,
nullHostBridge,
} from "../HostBridge";
import { type JoinCallData } from "../widget";
import { MatrixRTCTransportMissingError } from "../utils/errors";
import { ProcessorProvider } from "../livekit/TrackProcessorContext";
import { MediaDevicesContext } from "../MediaDevicesContext";
@@ -139,6 +143,8 @@ function createCallView(
joined = true,
options: {
withErrorBoundary?: boolean;
/** Wait for the host to say when to join, rather than joining at once. */
preload?: boolean;
} = {},
): {
rtcSession: MatrixRTCSession;
@@ -176,7 +182,7 @@ function createCallView(
client={client}
isPasswordlessUser={false}
confineToRoom={false}
preload={false}
preload={options.preload ?? false}
// Straight into the (mocked) call, past the lobby
skipLobby
rtcSession={rtcSession.asMockedSession()}
@@ -283,6 +289,26 @@ test("Should ask the host to close when all other left and play a sound", async
await waitFor(() => expect(close).toHaveBeenCalledOnce());
}, 80000);
test("Waits for the host to say when to join, when preloaded", async () => {
// Nothing to match device names against; the host names none anyway
vi.spyOn(LivekitRoom, "getLocalDevices").mockResolvedValue([]);
const join$ = new Subject<HostRequest<JoinCallData>>();
const hostBridge: HostBridge = { ...nullHostBridge, join$ };
createCallView(hostBridge, false, { preload: true });
await flushPromises();
// Past the lobby, but not in the call: the host has not asked yet
expect(screen.queryByText("Leave")).toBeNull();
const reply = vi.fn();
act(() =>
join$.next({ data: { audioInput: null, videoInput: null }, reply }),
);
// Then in the call, and the host told so
await waitFor(() => expect(reply).toHaveBeenCalledOnce());
expect(screen.getByText("Leave")).toBeInTheDocument();
});
test("Should not ask the host to close when auto leave due to error", async () => {
const user = userEvent.setup();
+95
View File
@@ -0,0 +1,95 @@
/*
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, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { TooltipProvider } from "@vector-im/compound-web";
import { type MatrixClient, type RoomSummary } from "matrix-js-sdk";
import { KnockLobbyView } from "./KnockLobbyView";
import { LeaveToHomeProvider } from "../LeaveToHomeContext";
import { MediaDevicesContext } from "../MediaDevicesContext";
import { type ProcessorState } from "../livekit/TrackProcessorContext";
import { mockMediaDevices } from "../utils/test";
vi.mock("@livekit/components-react", () => ({
usePreviewTracks: (): unknown[] => [],
}));
vi.mock("../livekit/TrackProcessorContext", () => ({
useTrackProcessor: (): ProcessorState => ({
supported: false,
processor: undefined,
}),
useTrackProcessorSync: (): void => {},
}));
vi.mock("react-use-measure", () => ({
default: (): [() => void, object] => [(): void => {}, {}],
}));
vi.mock("../settings/SettingsModal", () => ({
SettingsModal: (): null => null,
defaultSettingsTab: "general",
}));
const client = {
getUserId: () => "@user:example.org",
getDeviceId: () => "DEVICE",
} as Partial<MatrixClient> as MatrixClient;
// What peeking at a room we are not in tells us about it
const roomSummary = {
room_id: "!room:example.org",
name: "Knock Room",
"im.nheko.summary.encryption": "m.megolm.v1.aes-sha2",
} as Partial<RoomSummary> as RoomSummary;
function renderKnockLobby(knock: (() => void) | null): void {
render(
<LeaveToHomeProvider value={vi.fn()}>
<MediaDevicesContext value={mockMediaDevices({})}>
<TooltipProvider>
<KnockLobbyView
client={client}
roomSummary={roomSummary}
profile={{ displayName: "Test User", avatarUrl: "" }}
knock={knock}
confineToRoom={false}
hideHeader={false}
/>
</TooltipProvider>
</MediaDevicesContext>
</LeaveToHomeProvider>,
);
}
describe("KnockLobbyView", () => {
it("offers to ask to join, with what it knows of the room", async () => {
const knock = vi.fn();
renderKnockLobby(knock);
// The mute state arrives asynchronously, and the lobby with it
const button = await screen.findByTestId("lobby_joinCall");
expect(button).toHaveTextContent("Request to join call");
expect(button).toBeEnabled();
expect(screen.getByText("Knock Room")).toBeInTheDocument();
await userEvent.setup().click(button);
expect(knock).toHaveBeenCalledOnce();
});
it("waits once it has asked", async () => {
renderKnockLobby(null);
const button = await screen.findByTestId("lobby_joinCall");
expect(button).toHaveTextContent("Request sent!");
// Compound's button keeps focusable, saying so through ARIA instead
expect(button).toHaveAttribute("aria-disabled", "true");
});
});