Some simple initial tests for MediaView (#2813)

* Some simple tests for MediaView

* Use jest-dom assertions

* Add tests for videoMuted

* Add test case for placeholder video track

* Revert yarn.lock changes

* More revert

* Deduplicate test case logic and improve names

* Use role and label
This commit is contained in:
Hugh Nimmo-Smith
2024-11-25 20:22:02 +00:00
committed by GitHub
parent e36029c9c0
commit cf174261c9
7 changed files with 187 additions and 13 deletions

View File

@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { useMemo, FC } from "react";
import { useMemo, FC, CSSProperties } from "react";
import { Avatar as CompoundAvatar } from "@vector-im/compound-web";
import { getAvatarUrl } from "./utils/matrix";
@@ -33,6 +33,7 @@ interface Props {
className?: string;
src?: string;
size?: Size | number;
style?: CSSProperties;
}
export const Avatar: FC<Props> = ({
@@ -41,6 +42,8 @@ export const Avatar: FC<Props> = ({
name,
src,
size = Size.MD,
style,
...props
}) => {
const { client } = useClient();
@@ -64,6 +67,8 @@ export const Avatar: FC<Props> = ({
name={name}
size={`${sizePx}px`}
src={resolvedSrc}
style={style}
{...props}
/>
);
};

View File

@@ -34,10 +34,6 @@ Please see LICENSE in the repository root for full details.
object-fit: contain;
}
.media.videoMuted video {
display: none;
}
.bg {
background-color: var(--cpd-color-bg-subtle-secondary);
inline-size: 100%;
@@ -47,7 +43,6 @@ Please see LICENSE in the repository root for full details.
}
.avatar {
display: none;
position: absolute;
top: 50%;
left: 50%;
@@ -55,10 +50,6 @@ Please see LICENSE in the repository root for full details.
pointer-events: none;
}
.media.videoMuted .avatar {
display: initial;
}
/* CSS makes us put a condition here, even though all we want to do is
unconditionally select the container so we can use cqmin units */
@container mediaView (width > 0) {

117
src/tile/MediaView.test.tsx Normal file
View File

@@ -0,0 +1,117 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { describe, expect, test } from "vitest";
import { render, screen } from "@testing-library/react";
import { axe } from "vitest-axe";
import { TooltipProvider } from "@vector-im/compound-web";
import {
TrackReference,
TrackReferencePlaceholder,
} from "@livekit/components-core";
import { Track, TrackPublication } from "livekit-client";
import { type ComponentProps } from "react";
import { MediaView } from "./MediaView";
import { EncryptionStatus } from "../state/MediaViewModel";
import { mockLocalParticipant } from "../utils/test";
describe("MediaView", () => {
const participant = mockLocalParticipant({});
const trackReferencePlaceholder: TrackReferencePlaceholder = {
participant,
source: Track.Source.Camera,
};
const trackReference: TrackReference = {
...trackReferencePlaceholder,
publication: new TrackPublication(Track.Kind.Video, "id", "name"),
};
const baseProps: ComponentProps<typeof MediaView> = {
displayName: "some name",
videoEnabled: true,
videoFit: "contain",
targetWidth: 300,
targetHeight: 200,
encryptionStatus: EncryptionStatus.Connecting,
mirror: false,
unencryptedWarning: false,
video: trackReference,
member: undefined,
};
test("is accessible", async () => {
const { container } = render(<MediaView {...baseProps} />);
expect(await axe(container)).toHaveNoViolations();
});
describe("placeholder track", () => {
test("neither video nor avatar are shown", () => {
render(<MediaView {...baseProps} video={trackReferencePlaceholder} />);
expect(screen.queryByTestId("video")).toBeNull();
expect(screen.queryAllByRole("img", { name: "some name" }).length).toBe(
0,
);
});
});
describe("name tag", () => {
test("is shown with name", () => {
render(<MediaView {...baseProps} displayName="Bob" />);
expect(screen.getByTestId("name_tag")).toHaveTextContent("Bob");
});
});
describe("unencryptedWarning", () => {
test("is shown and accessible", async () => {
const { container } = render(
<TooltipProvider>
<MediaView {...baseProps} unencryptedWarning={true} />
</TooltipProvider>,
);
expect(await axe(container)).toHaveNoViolations();
expect(
screen.getByRole("img", { name: "common.unencrypted" }),
).toBeTruthy();
});
test("is not shown", () => {
render(
<TooltipProvider>
<MediaView {...baseProps} unencryptedWarning={false} />
</TooltipProvider>,
);
expect(
screen.queryAllByRole("img", { name: "common.unencrypted" }).length,
).toBe(0);
});
});
describe("videoEnabled", () => {
test("just video is visible", () => {
render(
<TooltipProvider>
<MediaView {...baseProps} videoEnabled={true} />
</TooltipProvider>,
);
expect(screen.getByTestId("video")).toBeVisible();
expect(screen.queryAllByRole("img", { name: "some name" }).length).toBe(
0,
);
});
test("just avatar is visible", () => {
render(
<TooltipProvider>
<MediaView {...baseProps} videoEnabled={false} />
</TooltipProvider>,
);
expect(screen.getByRole("img", { name: "some name" })).toBeVisible();
expect(screen.getByTestId("video")).not.toBeVisible();
});
});
});

View File

@@ -76,7 +76,6 @@ export const MediaView = forwardRef<HTMLDivElement, Props>(
<animated.div
className={classNames(styles.media, className, {
[styles.mirror]: mirror,
[styles.videoMuted]: !videoEnabled,
})}
style={style}
ref={ref}
@@ -91,6 +90,7 @@ export const MediaView = forwardRef<HTMLDivElement, Props>(
size={avatarSize}
src={member?.getMxcAvatarUrl()}
className={styles.avatar}
style={{ display: videoEnabled ? "none" : "initial" }}
/>
{video.publication !== undefined && (
<VideoTrack
@@ -98,6 +98,8 @@ export const MediaView = forwardRef<HTMLDivElement, Props>(
// There's no reason for this to be focusable
tabIndex={-1}
disablePictureInPicture
style={{ display: videoEnabled ? "block" : "none" }}
data-testid="video"
/>
)}
</div>
@@ -133,7 +135,13 @@ export const MediaView = forwardRef<HTMLDivElement, Props>(
)*/}
<div className={styles.nameTag}>
{nameTagLeadingIcon}
<Text as="span" size="sm" weight="medium" className={styles.name}>
<Text
as="span"
size="sm"
weight="medium"
className={styles.name}
data-testid="name_tag"
>
{displayName}
</Text>
{unencryptedWarning && (
@@ -146,6 +154,8 @@ export const MediaView = forwardRef<HTMLDivElement, Props>(
width={20}
height={20}
className={styles.errorIcon}
role="img"
aria-label={t("common.unencrypted")}
/>
</Tooltip>
)}

View File

@@ -15,6 +15,7 @@ import { afterEach } from "vitest";
import { cleanup } from "@testing-library/react";
import "vitest-axe/extend-expect";
import { logger } from "matrix-js-sdk/src/logger";
import "@testing-library/jest-dom/vitest";
import EN_GB from "../locales/en-GB/app.json";
import { Config } from "./config/Config";