/**
* Tests for the UserDropdown component
* @jest-environment jsdom
*/
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { UserDropdown } from '@/components/UserDropdown';
// Mock next/link
jest.mock('next/link', () => {
const MockLink = ({ children, href }: { children: React.ReactNode; href: string }) => (
{children}
);
MockLink.displayName = 'MockLink';
return MockLink;
});
describe('UserDropdown', () => {
const mockUser = {
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@example.com',
};
const mockOnLogout = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
});
it('should render user initials', () => {
render(
);
expect(screen.getByText('JD')).toBeInTheDocument();
});
it('should render user name and email', () => {
render(
);
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('john.doe@example.com')).toBeInTheDocument();
});
it('should toggle dropdown on click', () => {
render(
);
// Dropdown should be closed initially
expect(screen.queryByText('Profil')).not.toBeInTheDocument();
// Click to open - get the main trigger button (contains user name)
const triggerButton = screen.getByText('John Doe').closest('button');
fireEvent.click(triggerButton!);
expect(screen.getByText('Profil')).toBeInTheDocument();
expect(screen.getByText('Deconnexion')).toBeInTheDocument();
// Click to close
fireEvent.click(triggerButton!);
expect(screen.queryByText('Profil')).not.toBeInTheDocument();
});
it('should render profile link with correct path', () => {
render(
);
const triggerButton = screen.getByText('John Doe').closest('button');
fireEvent.click(triggerButton!);
const profileLink = screen.getByText('Profil').closest('a');
expect(profileLink).toHaveAttribute('href', '/admin/profil');
});
it('should call onLogout when logout button is clicked', () => {
render(
);
const triggerButton = screen.getByText('John Doe').closest('button');
fireEvent.click(triggerButton!);
fireEvent.click(screen.getByText('Deconnexion'));
expect(mockOnLogout).toHaveBeenCalledTimes(1);
});
it('should have onClick handler on profile link', () => {
render(
);
const triggerButton = screen.getByText('John Doe').closest('button');
fireEvent.click(triggerButton!);
const profileLink = screen.getByText('Profil').closest('a');
expect(profileLink).toBeInTheDocument();
expect(profileLink).toHaveAttribute('href', '/profil');
});
it('should apply blue accent color by default', () => {
const { container } = render(
);
const avatar = container.querySelector('.bg-blue-600');
expect(avatar).toBeInTheDocument();
});
it('should apply green accent color when specified', () => {
const { container } = render(
);
const avatar = container.querySelector('.bg-green-600');
expect(avatar).toBeInTheDocument();
});
it('should handle null user gracefully', () => {
const { container } = render(
);
// Should render empty initials
const avatarDiv = container.querySelector('.bg-blue-600');
expect(avatarDiv).toBeInTheDocument();
});
it('should handle user with missing firstName', () => {
render(
);
expect(screen.getByText('D')).toBeInTheDocument();
});
it('should apply custom className', () => {
const { container } = render(
);
expect(container.firstChild).toHaveClass('custom-class');
});
it('should rotate chevron icon when dropdown is open', () => {
const { container } = render(
);
const chevron = container.querySelector('.w-4.h-4.text-gray-500');
expect(chevron).not.toHaveClass('rotate-180');
const triggerButton = screen.getByText('John Doe').closest('button');
fireEvent.click(triggerButton!);
const rotatedChevron = container.querySelector('.rotate-180');
expect(rotatedChevron).toBeInTheDocument();
});
});