Remember previous mute states when devices disappear and reappear (#2957)
This commit is contained in:
@@ -6,9 +6,10 @@ Please see LICENSE in the repository root for full details.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { afterAll, afterEach, describe, expect, it, vi } from "vitest";
|
import { afterAll, afterEach, describe, expect, it, vi } from "vitest";
|
||||||
import { type ReactNode } from "react";
|
import { type FC, useCallback, useState, type ReactNode } from "react";
|
||||||
import { render, screen } from "@testing-library/react";
|
import { render, screen } from "@testing-library/react";
|
||||||
import { MemoryRouter } from "react-router-dom";
|
import { MemoryRouter } from "react-router-dom";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
|
||||||
import { useMuteStates } from "./MuteStates";
|
import { useMuteStates } from "./MuteStates";
|
||||||
import {
|
import {
|
||||||
@@ -21,11 +22,16 @@ import { mockConfig } from "../utils/test";
|
|||||||
|
|
||||||
function TestComponent(): ReactNode {
|
function TestComponent(): ReactNode {
|
||||||
const muteStates = useMuteStates();
|
const muteStates = useMuteStates();
|
||||||
|
const onToggleAudio = useCallback(
|
||||||
|
() => muteStates.audio.setEnabled?.(!muteStates.audio.enabled),
|
||||||
|
[muteStates],
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div data-testid="audio-enabled">
|
<div data-testid="audio-enabled">
|
||||||
{muteStates.audio.enabled.toString()}
|
{muteStates.audio.enabled.toString()}
|
||||||
</div>
|
</div>
|
||||||
|
<button onClick={onToggleAudio}>Toggle audio</button>
|
||||||
<div data-testid="video-enabled">
|
<div data-testid="video-enabled">
|
||||||
{muteStates.video.enabled.toString()}
|
{muteStates.video.enabled.toString()}
|
||||||
</div>
|
</div>
|
||||||
@@ -174,4 +180,50 @@ describe("useMuteStates", () => {
|
|||||||
expect(screen.getByTestId("audio-enabled").textContent).toBe("false");
|
expect(screen.getByTestId("audio-enabled").textContent).toBe("false");
|
||||||
expect(screen.getByTestId("video-enabled").textContent).toBe("false");
|
expect(screen.getByTestId("video-enabled").textContent).toBe("false");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("remembers previous state when devices disappear and reappear", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
mockConfig();
|
||||||
|
const noDevices = mockMediaDevices({ microphone: false, camera: false });
|
||||||
|
const someDevices = mockMediaDevices();
|
||||||
|
const ReappearanceTest: FC = () => {
|
||||||
|
const [devices, setDevices] = useState(someDevices);
|
||||||
|
const onConnectDevicesClick = useCallback(
|
||||||
|
() => setDevices(someDevices),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const onDisconnectDevicesClick = useCallback(
|
||||||
|
() => setDevices(noDevices),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MemoryRouter>
|
||||||
|
<MediaDevicesContext.Provider value={devices}>
|
||||||
|
<TestComponent />
|
||||||
|
<button onClick={onConnectDevicesClick}>Connect devices</button>
|
||||||
|
<button onClick={onDisconnectDevicesClick}>
|
||||||
|
Disconnect devices
|
||||||
|
</button>
|
||||||
|
</MediaDevicesContext.Provider>
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
render(<ReappearanceTest />);
|
||||||
|
expect(screen.getByTestId("audio-enabled").textContent).toBe("true");
|
||||||
|
expect(screen.getByTestId("video-enabled").textContent).toBe("true");
|
||||||
|
await user.click(screen.getByRole("button", { name: "Toggle audio" }));
|
||||||
|
expect(screen.getByTestId("audio-enabled").textContent).toBe("false");
|
||||||
|
expect(screen.getByTestId("video-enabled").textContent).toBe("true");
|
||||||
|
await user.click(
|
||||||
|
screen.getByRole("button", { name: "Disconnect devices" }),
|
||||||
|
);
|
||||||
|
expect(screen.getByTestId("audio-enabled").textContent).toBe("false");
|
||||||
|
expect(screen.getByTestId("video-enabled").textContent).toBe("false");
|
||||||
|
await user.click(screen.getByRole("button", { name: "Connect devices" }));
|
||||||
|
// Audio should remember that it was muted, while video should re-enable
|
||||||
|
expect(screen.getByTestId("audio-enabled").textContent).toBe("false");
|
||||||
|
expect(screen.getByTestId("video-enabled").textContent).toBe("true");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -57,8 +57,9 @@ function useMuteState(
|
|||||||
enabledByDefault: () => boolean,
|
enabledByDefault: () => boolean,
|
||||||
): MuteState {
|
): MuteState {
|
||||||
const [enabled, setEnabled] = useReactiveState<boolean | undefined>(
|
const [enabled, setEnabled] = useReactiveState<boolean | undefined>(
|
||||||
|
// Determine the default value once devices are actually connected
|
||||||
(prev) =>
|
(prev) =>
|
||||||
device.available.size > 0 ? (prev ?? enabledByDefault()) : undefined,
|
prev ?? (device.available.size > 0 ? enabledByDefault() : undefined),
|
||||||
[device],
|
[device],
|
||||||
);
|
);
|
||||||
return useMemo(
|
return useMemo(
|
||||||
|
|||||||
Reference in New Issue
Block a user