184 lines
5.9 KiB
TypeScript
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');
|
|
});
|
|
});
|