veza/apps/web/src/components/navigation/Breadcrumbs.test.tsx
2025-12-12 21:34:34 -05:00

184 lines
5.9 KiB
TypeScript

import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { MemoryRouter } from 'react-router-dom';
import { Breadcrumbs } from './Breadcrumbs';
// Mock react-router-dom
vi.mock('react-router-dom', async () => {
const actual = await vi.importActual('react-router-dom');
return {
...actual,
Link: ({ to, children, ...props }: any) => (
<a href={to} {...props}>
{children}
</a>
),
};
});
const renderWithRouter = (component: React.ReactElement) => {
return render(<MemoryRouter>{component}</MemoryRouter>);
};
describe('Breadcrumbs Component', () => {
const mockItems = [
{ label: 'Dashboard', href: '/dashboard' },
{ label: 'Settings', href: '/dashboard/settings' },
{ label: 'Profile' },
];
it('renders breadcrumbs with items', () => {
renderWithRouter(<Breadcrumbs items={mockItems} />);
expect(screen.getByText('Dashboard')).toBeInTheDocument();
expect(screen.getByText('Settings')).toBeInTheDocument();
expect(screen.getByText('Profile')).toBeInTheDocument();
});
it('shows home link by default', () => {
renderWithRouter(<Breadcrumbs items={mockItems} />);
expect(screen.getByText('Home')).toBeInTheDocument();
});
it('hides home link when showHome is false', () => {
renderWithRouter(<Breadcrumbs items={mockItems} showHome={false} />);
expect(screen.queryByText('Home')).not.toBeInTheDocument();
});
it('renders links for items with href', () => {
renderWithRouter(<Breadcrumbs items={mockItems} showHome={false} />);
const dashboardLink = screen.getByText('Dashboard').closest('a');
expect(dashboardLink).toHaveAttribute('href', '/dashboard');
const settingsLink = screen.getByText('Settings').closest('a');
expect(settingsLink).toHaveAttribute('href', '/dashboard/settings');
});
it('renders span for last item without href', () => {
renderWithRouter(<Breadcrumbs items={mockItems} showHome={false} />);
const profileElement = screen.getByText('Profile');
expect(profileElement.closest('a')).toBeNull();
expect(profileElement.tagName).toBe('SPAN');
const parentSpan = profileElement.closest('span');
expect(parentSpan).toHaveAttribute('aria-current', 'page');
});
it('renders separators between items', () => {
const { container } = renderWithRouter(
<Breadcrumbs items={mockItems} showHome={false} />,
);
const separators = container.querySelectorAll('[aria-hidden="true"]');
// 2 items = 1 separator
expect(separators.length).toBeGreaterThan(0);
});
it('does not render separator after last item', () => {
renderWithRouter(<Breadcrumbs items={mockItems} showHome={false} />);
const profileElement = screen.getByText('Profile');
const parentLi = profileElement.closest('li');
const nextSibling = parentLi?.nextElementSibling;
// Le séparateur ne devrait pas être après le dernier élément
if (nextSibling) {
expect(nextSibling).not.toHaveAttribute('aria-hidden', 'true');
} else {
// Pas de nextSibling, donc pas de séparateur après
expect(nextSibling).toBeNull();
}
});
it('uses custom home href', () => {
renderWithRouter(<Breadcrumbs items={mockItems} homeHref="/custom-home" />);
const homeLink = screen.getByText('Home').closest('a');
expect(homeLink).toHaveAttribute('href', '/custom-home');
});
it('renders custom separator', () => {
const customSeparator = <span>/</span>;
renderWithRouter(
<Breadcrumbs
items={mockItems}
separator={customSeparator}
showHome={false}
/>,
);
const separators = screen.getAllByText('/');
expect(separators.length).toBeGreaterThan(0);
});
it('renders item with icon', () => {
const itemsWithIcon = [
{
label: 'Dashboard',
href: '/dashboard',
icon: <span data-testid="icon">📊</span>,
},
];
renderWithRouter(<Breadcrumbs items={itemsWithIcon} showHome={false} />);
expect(screen.getByTestId('icon')).toBeInTheDocument();
});
it('renders home icon by default', () => {
renderWithRouter(<Breadcrumbs items={mockItems} />);
// L'icône Home devrait être présente (lucide-react Home icon)
const homeElement = screen.getByText('Home').closest('a');
expect(homeElement).toBeInTheDocument();
});
it('applies custom className', () => {
const { container } = renderWithRouter(
<Breadcrumbs items={mockItems} className="custom-class" />,
);
const nav = container.querySelector('nav');
expect(nav).toHaveClass('custom-class');
});
it('handles empty items array', () => {
renderWithRouter(<Breadcrumbs items={[]} showHome={true} />);
expect(screen.getByText('Home')).toBeInTheDocument();
});
it('handles single item', () => {
const singleItem = [{ label: 'Page', href: '/page' }];
renderWithRouter(<Breadcrumbs items={singleItem} showHome={false} />);
expect(screen.getByText('Page')).toBeInTheDocument();
const separators = screen.queryAllByRole('separator', { hidden: true });
expect(separators.length).toBe(0);
});
it('has correct aria-label for navigation', () => {
const { container } = renderWithRouter(<Breadcrumbs items={mockItems} />);
const nav = container.querySelector('nav');
expect(nav).toHaveAttribute('aria-label', 'Breadcrumb');
});
it('applies correct styles to last item', () => {
renderWithRouter(<Breadcrumbs items={mockItems} showHome={false} />);
const profileElement = screen.getByText('Profile');
const parentSpan = profileElement.closest('span');
expect(parentSpan).toHaveClass('text-foreground');
});
it('applies correct styles to non-last items', () => {
renderWithRouter(<Breadcrumbs items={mockItems} showHome={false} />);
const dashboardElement = screen.getByText('Dashboard');
const linkElement = dashboardElement.closest('a');
expect(linkElement).toHaveClass('text-muted-foreground');
});
});