jest.mock('../../../src/renderer/lib/utils', () => ({
cn: (...classes: string[]) => classes.join(' '),
}));
jest.mock('@radix-ui/react-avatar', () => ({
Root: ({ children, className, ...props }: any) =>
React.createElement(
'div',
{ 'data-testid': 'avatar-root', className, ...props },
children
),
Image: ({ className, ...props }: any) =>
React.createElement('img', {
'data-testid': 'avatar-image',
className,
...props,
}),
Fallback: ({ children, className, ...props }: any) =>
React.createElement(
'div',
{ 'data-testid': 'avatar-fallback', className, ...props },
children
),
}));
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import {
Avatar,
AvatarImage,
AvatarFallback,
} from '../../../src/renderer/components/ui/avatar';
describe('Avatar Component', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('Avatar Root', () => {
test('renders with default props', () => {
render();
const avatar = screen.getByTestId('avatar-root');
expect(avatar).toBeInTheDocument();
expect(avatar).toHaveAttribute('data-slot', 'avatar');
expect(avatar).toHaveClass('relative');
expect(avatar).toHaveClass('flex');
expect(avatar).toHaveClass('size-8');
expect(avatar).toHaveClass('shrink-0');
expect(avatar).toHaveClass('overflow-hidden');
expect(avatar).toHaveClass('rounded-full');
});
test('applies custom className', () => {
render();
const avatar = screen.getByTestId('avatar-root');
expect(avatar).toHaveClass('custom-class');
});
test('forwards additional props', () => {
render();
expect(screen.getByTestId('custom-avatar')).toBeInTheDocument();
});
test('has correct accessibility attributes', () => {
render();
const avatar = screen.getByTestId('avatar-root');
expect(avatar).toHaveAttribute('role', 'img');
expect(avatar).toHaveAttribute('aria-label', 'User avatar');
});
});
describe('AvatarImage', () => {
test('renders with src and alt', () => {
render(
);
const image = screen.getByTestId('avatar-image');
expect(image).toBeInTheDocument();
expect(image).toHaveAttribute('src', 'https://example.com/avatar.jpg');
expect(image).toHaveAttribute('alt', 'User Avatar');
expect(image).toHaveClass('aspect-square');
expect(image).toHaveClass('size-full');
});
test('applies custom className', () => {
render(
);
const image = screen.getByTestId('avatar-image');
expect(image).toHaveClass('custom-image');
});
test('forwards additional props', () => {
render(
);
const image = screen.getByTestId('avatar-image');
expect(image).toHaveAttribute('loading', 'lazy');
});
test('handles missing src gracefully', () => {
render(
);
const image = screen.getByTestId('avatar-image');
expect(image).toBeInTheDocument();
expect(image).toHaveAttribute('alt', 'Fallback');
});
});
describe('AvatarFallback', () => {
test('renders with children', () => {
render(
JD
);
const fallback = screen.getByTestId('avatar-fallback');
expect(fallback).toBeInTheDocument();
expect(fallback).toHaveTextContent('JD');
expect(fallback).toHaveClass('bg-muted');
expect(fallback).toHaveClass('flex');
expect(fallback).toHaveClass('size-full');
expect(fallback).toHaveClass('items-center');
expect(fallback).toHaveClass('justify-center');
expect(fallback).toHaveClass('rounded-full');
});
test('applies custom className', () => {
render(
AB
);
const fallback = screen.getByTestId('avatar-fallback');
expect(fallback).toHaveClass('custom-fallback');
});
test('handles complex children', () => {
render(
User
Name
);
const fallback = screen.getByTestId('avatar-fallback');
expect(fallback).toHaveTextContent('User');
expect(fallback).toHaveTextContent('Name');
});
test('renders with emoji', () => {
render(
👤
);
const fallback = screen.getByTestId('avatar-fallback');
expect(fallback).toHaveTextContent('👤');
});
test('forwards additional props', () => {
render(
Test
);
expect(screen.getByTestId('custom-fallback')).toBeInTheDocument();
});
});
describe('Complete Avatar Usage', () => {
test('renders with image and fallback', () => {
render(
UN
);
expect(screen.getByTestId('avatar-root')).toBeInTheDocument();
expect(screen.getByTestId('avatar-image')).toBeInTheDocument();
expect(screen.getByTestId('avatar-fallback')).toBeInTheDocument();
});
test('renders fallback when image fails', () => {
render(
BF
);
expect(screen.getByTestId('avatar-image')).toBeInTheDocument();
expect(screen.getByTestId('avatar-fallback')).toBeInTheDocument();
expect(screen.getByText('BF')).toBeInTheDocument();
});
test('renders only fallback when no image', () => {
render(
OF
);
expect(screen.queryByTestId('avatar-image')).not.toBeInTheDocument();
expect(screen.getByTestId('avatar-fallback')).toBeInTheDocument();
expect(screen.getByText('OF')).toBeInTheDocument();
});
test('renders only image when no fallback', () => {
render(
);
expect(screen.getByTestId('avatar-image')).toBeInTheDocument();
expect(screen.queryByTestId('avatar-fallback')).not.toBeInTheDocument();
});
});
describe('Data Attributes', () => {
test('Avatar has correct data-slot', () => {
render();
const avatar = screen.getByTestId('avatar-root');
expect(avatar).toHaveAttribute('data-slot', 'avatar');
});
test('AvatarImage has correct data-slot', () => {
render(
);
const image = screen.getByTestId('avatar-image');
expect(image).toHaveAttribute('data-slot', 'avatar-image');
});
test('AvatarFallback has correct data-slot', () => {
render(
Test
);
const fallback = screen.getByTestId('avatar-fallback');
expect(fallback).toHaveAttribute('data-slot', 'avatar-fallback');
});
});
describe('Styling and Classes', () => {
test('Avatar applies base classes correctly', () => {
render();
const avatar = screen.getByTestId('avatar-root');
expect(avatar).toHaveClass(
'relative',
'flex',
'size-8',
'shrink-0',
'overflow-hidden',
'rounded-full'
);
});
test('AvatarImage applies base classes correctly', () => {
render(
);
const image = screen.getByTestId('avatar-image');
expect(image).toHaveClass('aspect-square', 'size-full');
});
test('AvatarFallback applies base classes correctly', () => {
render(
Test
);
const fallback = screen.getByTestId('avatar-fallback');
expect(fallback).toHaveClass(
'bg-muted',
'flex',
'size-full',
'items-center',
'justify-center',
'rounded-full'
);
});
test('custom classes are merged correctly', () => {
render(
T
);
const avatar = screen.getByTestId('avatar-root');
const image = screen.getByTestId('avatar-image');
const fallback = screen.getByTestId('avatar-fallback');
expect(avatar).toHaveClass('w-12', 'h-12');
expect(image).toHaveClass('object-cover');
expect(fallback).toHaveClass('text-lg');
});
});
describe('Edge Cases', () => {
test('handles empty children', () => {
render();
const avatar = screen.getByTestId('avatar-root');
expect(avatar).toBeInTheDocument();
expect(avatar.children).toHaveLength(0);
});
test('handles multiple images (should not happen but test robustness)', () => {
render(
);
const images = screen.getAllByTestId('avatar-image');
expect(images).toHaveLength(2);
});
test('handles multiple fallbacks (should not happen but test robustness)', () => {
render(
F1
F2
);
const fallbacks = screen.getAllByTestId('avatar-fallback');
expect(fallbacks).toHaveLength(2);
expect(screen.getByText('F1')).toBeInTheDocument();
expect(screen.getByText('F2')).toBeInTheDocument();
});
test('handles undefined props gracefully', () => {
render(
Test
);
expect(screen.getByTestId('avatar-root')).toBeInTheDocument();
expect(screen.getByTestId('avatar-image')).toBeInTheDocument();
expect(screen.getByTestId('avatar-fallback')).toBeInTheDocument();
expect(screen.getByText('Test')).toBeInTheDocument();
});
});
describe('Integration with User Data', () => {
test('can be used with user profile data', () => {
const userData = {
name: 'John Doe',
avatar: 'https://example.com/avatar.jpg',
initials: 'JD',
};
render(
{userData.initials}
);
const image = screen.getByTestId('avatar-image');
const fallback = screen.getByTestId('avatar-fallback');
expect(image).toHaveAttribute('src', userData.avatar);
expect(image).toHaveAttribute('alt', userData.name);
expect(fallback).toHaveTextContent(userData.initials);
});
test('handles user data with missing avatar', () => {
const userData = {
name: 'Jane Smith',
initials: 'JS',
};
render(
{userData.initials}
);
expect(screen.queryByTestId('avatar-image')).not.toBeInTheDocument();
expect(screen.getByTestId('avatar-fallback')).toBeInTheDocument();
expect(screen.getByText(userData.initials)).toBeInTheDocument();
});
});
describe('Accessibility', () => {
test('supports screen reader with proper labeling', () => {
render(
UP
);
const avatar = screen.getByTestId('avatar-root');
expect(avatar).toHaveAttribute('aria-label', 'User profile picture');
const image = screen.getByTestId('avatar-image');
expect(image).toHaveAttribute('alt', 'Profile picture');
});
test('supports role attributes', () => {
render(
Click me
);
const avatar = screen.getByTestId('avatar-root');
expect(avatar).toHaveAttribute('role', 'button');
expect(avatar).toHaveAttribute('tabIndex', '0');
});
});
});