Files
element-call/src/components/MicrophoneLevelMeter.test.tsx
T
fkwpandClaude Opus 5 c9bfe9df04 Add a microphone level meter
- Draws the level as lit bars, and each failure state as its own message with a
  next action rather than as a meter that has stopped.
- A bar and the space beside it keep one size wherever the meter is drawn; what
  the width decides is how many bars there are. Bars spread to fill would be a
  different shape in every place, and would close into one block where the space
  ran short.
- Announces the level on a fixed scale, so it does not rest on hue or on how
  many bars happened to fit.
- The hook is a bridge and nothing more: the capture belongs to the state layer.
- The capture is held by a component of its own, so a level that does change
  redraws the meter rather than whatever is rendered beside it. A level arrives
  many times a second; held by the menu it would reconcile every device row on
  the way to the bars. A test counts those renders, because nothing else would
  notice the hook moving back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 14:56:47 +02:00

41 lines
1.5 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, test } from "vitest";
import { render, screen } from "@testing-library/react";
import { MicrophoneLevelMeter } from "./MicrophoneLevelMeter";
import { LEVEL_SCALE } from "../state/MicrophoneLevel";
describe("MicrophoneLevelMeter", () => {
test("announces the level rather than relying on hue", () => {
render(<MicrophoneLevelMeter state={{ type: "level", level: 6 }} />);
const meter = screen.getByRole("meter", { name: "Microphone level" });
expect(meter).toHaveAttribute("aria-valuenow", "6");
expect(meter).toHaveAttribute("aria-valuemax", String(LEVEL_SCALE));
expect(meter).toHaveAttribute("aria-valuetext", `6 of ${LEVEL_SCALE}`);
});
test("shows distinct messages for denied permission and no input device", () => {
const denied = render(
<MicrophoneLevelMeter state={{ type: "permission-denied" }} />,
);
// A next action, not a flat meter that reads as silence.
expect(
denied.getByText(/Allow access in your browser settings/),
).toBeInTheDocument();
expect(denied.queryByRole("meter")).toBeNull();
const missing = render(
<MicrophoneLevelMeter state={{ type: "no-device" }} />,
);
expect(missing.getByText(/No microphone found/)).toBeInTheDocument();
expect(missing.queryByRole("meter")).toBeNull();
});
});