import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { describe, it, expect, vi } from 'vitest'
import { axe } from 'vitest-axe'
import {
Breadcrumb,
BreadcrumbList,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbSeparator,
BreadcrumbEllipsis,
} from './breadcrumb'
function renderBasic() {
return render(
Home
Components
Breadcrumb
,
)
}
describe('Breadcrumb', () => {
it('renders a nav with breadcrumb label and an ordered list', () => {
renderBasic()
const nav = screen.getByRole('navigation', { name: 'breadcrumb' })
expect(nav.tagName).toBe('NAV')
expect(nav).toHaveAttribute('data-slot', 'breadcrumb')
const list = screen.getByRole('list')
expect(list.tagName).toBe('OL')
expect(list).toHaveAttribute('data-slot', 'breadcrumb-list')
})
it('renders BreadcrumbItem as li', () => {
renderBasic()
const items = screen.getAllByRole('listitem')
expect(items.length).toBeGreaterThan(0)
items.forEach((li) => expect(li.tagName).toBe('LI'))
})
it('renders an inactive link as and fires onClick', async () => {
const user = userEvent.setup()
const onClick = vi.fn()
render(
Home
,
)
const link = screen.getByRole('link', { name: 'Home' })
expect(link.tagName).toBe('A')
await user.click(link)
expect(onClick).toHaveBeenCalledOnce()
})
it('renders an active link as a non-focusable with aria-current="page" and data-active', () => {
renderBasic()
const active = screen.getByText('Breadcrumb')
expect(active.tagName).toBe('SPAN')
expect(active).toHaveAttribute('aria-current', 'page')
expect(active).toHaveAttribute('aria-disabled', 'true')
expect(active).toHaveAttribute('data-active')
expect(active).toHaveAttribute('tabindex', '-1')
})
it('marks a disabled link with aria-disabled and data-disabled', () => {
render(
Frozen
,
)
const link = screen.getByRole('link', { name: 'Frozen' })
expect(link).toHaveAttribute('aria-disabled', 'true')
expect(link).toHaveAttribute('data-disabled')
})
it('forwards className to the link alongside default classes', () => {
render(
Home
,
)
expect(screen.getByRole('link', { name: 'Home' }).className).toContain('my-link')
})
it('renders the link as a different element via render prop', () => {
const onClick = vi.fn()
render(
}>Settings
,
)
const el = screen.getByRole('button', { name: 'Settings' })
expect(el.tagName).toBe('BUTTON')
})
it('renders a default separator svg, overridable via children', () => {
const { rerender } = render(
A
,
)
const sep = screen.getByTestId('sep')
expect(sep).toHaveAttribute('data-slot', 'breadcrumb-separator')
expect(sep.querySelector('svg')).not.toBeNull()
rerender(
A
/
,
)
const sepAgain = screen.getByTestId('sep')
expect(sepAgain.querySelector('svg')).toBeNull()
expect(sepAgain).toHaveTextContent('/')
})
it('renders the ellipsis as a decorative (aria-hidden) element', () => {
renderBasic()
const ellipsis = document.querySelector('[data-slot="breadcrumb-ellipsis"]')
expect(ellipsis).not.toBeNull()
expect(ellipsis).toHaveAttribute('aria-hidden')
})
it('has no accessibility violations', async () => {
const { container } = renderBasic()
expect(await axe(container)).toHaveNoViolations()
})
})