import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { describe, expect, it } from 'vitest' import { axe } from 'vitest-axe' import { Button } from '../button/button' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './tooltip' function renderTooltip( content = 'Add to library', { side, delay }: { side?: 'top' | 'bottom' | 'left' | 'right'; delay?: number } = {}, ) { return render( Hover} />

{content}

, ) } describe('Tooltip', () => { it('renders the trigger and tags it with data-slot', () => { renderTooltip() const trigger = screen.getByRole('button', { name: 'Hover' }) expect(trigger.getAttribute('data-slot')).toBe('tooltip-trigger') }) it('does not render content before hover', () => { renderTooltip('Hidden until hover') expect(screen.queryByText('Hidden until hover')).toBeNull() }) it('shows content on hover and hides on unhover', async () => { const user = userEvent.setup() renderTooltip('Add to library') await user.hover(screen.getByRole('button', { name: 'Hover' })) const content = await screen.findByText('Add to library') expect(content).toBeTruthy() const popup = content.closest('[data-slot="tooltip-content"]') as HTMLElement expect(popup).not.toBeNull() expect(popup.className).toContain('bg-background-inverse') expect(popup.className).toContain('text-foreground-inverse') expect(popup.className).toContain('rounded-xs') expect(popup.className).toContain('text-xs') await user.unhover(screen.getByRole('button', { name: 'Hover' })) await waitFor(() => { expect(screen.queryByText('Add to library')).toBeNull() }) }) it('renders the arrow inside the popup with rotated-square classes', async () => { const user = userEvent.setup() renderTooltip() await user.hover(screen.getByRole('button', { name: 'Hover' })) await screen.findByText('Add to library') // Popup is portaled - query at document level. const arrow = document.querySelector('[data-slot="tooltip-arrow"]') as HTMLElement | null expect(arrow).not.toBeNull() expect(arrow!.className).toContain('size-2.5') expect(arrow!.className).toContain('rotate-45') expect(arrow!.className).toContain('bg-background-inverse') }) it('omits the arrow when arrow is false', async () => { const user = userEvent.setup() render( Hover} />

No arrow here.

, ) await user.hover(screen.getByRole('button', { name: 'Hover' })) await screen.findByText('No arrow here.') expect(document.querySelector('[data-slot="tooltip-arrow"]')).toBeNull() }) it('shows on focus (keyboard)', async () => { const user = userEvent.setup() renderTooltip('Keyboard-visible') await user.tab() expect(await screen.findByText('Keyboard-visible')).toBeTruthy() }) it('has no accessibility violations (closed state)', async () => { const { container } = renderTooltip() expect(await axe(container)).toHaveNoViolations() }) })