Give Avatar initials, person-mark, and load-error fallbacks so missing photos still look like the design-system atom.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import React from "react";
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import {
|
||||
renderWithProviders as render,
|
||||
screen,
|
||||
fireEvent,
|
||||
} from "../utils/test-utils";
|
||||
import Avatar from "../../app/components/asset/Avatar";
|
||||
import {
|
||||
componentTestSuite,
|
||||
type ComponentTestSuiteConfig,
|
||||
} from "../utils/componentTestSuite";
|
||||
|
||||
type AvatarProps = React.ComponentProps<typeof Avatar>;
|
||||
|
||||
const baseProps: AvatarProps = {
|
||||
src: "/assets/marketing/avatar-1.svg",
|
||||
alt: "User avatar",
|
||||
size: "medium",
|
||||
};
|
||||
|
||||
const config: ComponentTestSuiteConfig<AvatarProps> = {
|
||||
component: Avatar,
|
||||
name: "Avatar",
|
||||
props: baseProps,
|
||||
primaryRole: "img",
|
||||
testCases: {
|
||||
renders: true,
|
||||
accessibility: true,
|
||||
keyboardNavigation: false,
|
||||
disabledState: false,
|
||||
errorState: false,
|
||||
},
|
||||
};
|
||||
|
||||
componentTestSuite<AvatarProps>(config);
|
||||
|
||||
describe("Avatar (behavioral tests)", () => {
|
||||
it("renders the image with alt text", () => {
|
||||
render(<Avatar {...baseProps} />);
|
||||
const img = screen.getByRole("img", { name: "User avatar" });
|
||||
expect(img).toHaveAttribute("src", baseProps.src);
|
||||
});
|
||||
|
||||
it("does not show initials while the image is loading", () => {
|
||||
render(
|
||||
<Avatar
|
||||
src="/assets/marketing/avatar-1.svg"
|
||||
alt="Ada Lovelace"
|
||||
initials="AL"
|
||||
/>,
|
||||
);
|
||||
expect(screen.queryByText("AL")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("applies the small photo ring after the image loads", () => {
|
||||
const { container } = render(
|
||||
<Avatar
|
||||
src="/assets/marketing/avatar-1.svg"
|
||||
alt="User avatar"
|
||||
size="small"
|
||||
/>,
|
||||
);
|
||||
fireEvent.load(screen.getByRole("img", { name: "User avatar" }));
|
||||
expect(container.firstChild).toHaveClass("border-[#FFFFFF4D]");
|
||||
expect(container.firstChild).toHaveClass("box-border");
|
||||
});
|
||||
|
||||
it("shows initials when src is omitted", () => {
|
||||
render(<Avatar alt="Ada Lovelace" initials="Ada Lovelace" />);
|
||||
expect(screen.getByRole("img", { name: "Ada Lovelace" })).toHaveTextContent(
|
||||
"AL",
|
||||
);
|
||||
expect(document.querySelector("img")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows a person mark when src and initials are omitted", () => {
|
||||
const { container } = render(<Avatar alt="Member" />);
|
||||
expect(screen.getByRole("img", { name: "Member" })).toBeInTheDocument();
|
||||
expect(container.querySelector("svg")).toBeInTheDocument();
|
||||
expect(document.querySelector("img")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("switches to initials when the image errors", () => {
|
||||
render(
|
||||
<Avatar
|
||||
src="/missing-avatar.png"
|
||||
alt="Ada Lovelace"
|
||||
initials="AL"
|
||||
size="large"
|
||||
/>,
|
||||
);
|
||||
fireEvent.error(screen.getByRole("img", { name: "Ada Lovelace" }));
|
||||
expect(document.querySelector("img")).not.toBeInTheDocument();
|
||||
expect(screen.getByRole("img", { name: "Ada Lovelace" })).toHaveTextContent(
|
||||
"AL",
|
||||
);
|
||||
});
|
||||
|
||||
it("applies rounded-square radius", () => {
|
||||
const { container } = render(
|
||||
<Avatar alt="Org" initials="CR" shape="rounded-square" />,
|
||||
);
|
||||
expect(container.firstChild).toHaveClass(
|
||||
"rounded-[var(--measures-radius-200,8px)]",
|
||||
);
|
||||
});
|
||||
|
||||
it("applies outlined fallback chrome", () => {
|
||||
render(
|
||||
<Avatar alt="Ada Lovelace" initials="AL" variant="outlined" />,
|
||||
);
|
||||
const mark = screen.getByText("AL");
|
||||
expect(mark).toHaveClass(
|
||||
"border-[var(--color-content-default-brand-primary)]",
|
||||
);
|
||||
});
|
||||
|
||||
it("renders a button with hover treatment when onClick is set", () => {
|
||||
const onClick = vi.fn();
|
||||
render(
|
||||
<Avatar alt="Ada Lovelace" initials="AL" onClick={onClick} />,
|
||||
);
|
||||
const button = screen.getByRole("button", { name: "Ada Lovelace" });
|
||||
fireEvent.click(button);
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
expect(button).toHaveClass("hover:opacity-90");
|
||||
});
|
||||
|
||||
it("hides decorative fallbacks from the accessibility tree", () => {
|
||||
render(<Avatar alt="" initials="AB" />);
|
||||
expect(screen.queryByRole("img")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user