import { render, screen, fireEvent, act } from '@testing-library/react'; import { vi, beforeEach, afterEach, test, expect } from 'vitest'; import Tooltips, { tipFor } from './Tooltips.jsx'; beforeEach(() => vi.useFakeTimers()); afterEach(() => vi.useRealTimers()); const over = (el) => act(() => { fireEvent.pointerOver(el); vi.advanceTimersByTime(600); }); function setup() { render( <> , ); } test('shows a data-tip after a short pause, and hides when the pointer leaves', () => { setup(); const btn = screen.getByLabelText('Search'); over(btn); expect(screen.getByRole('tooltip')).toHaveTextContent('Search (/)'); expect(btn).toHaveAttribute('aria-describedby', 'app-tooltip'); act(() => { fireEvent.pointerOut(btn); }); expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); expect(btn).not.toHaveAttribute('aria-describedby'); }); test('does not appear before the pause', () => { setup(); act(() => { fireEvent.pointerOver(screen.getByLabelText('Search')); vi.advanceTimersByTime(100); }); expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); }); test('an icon-only button shows its label', () => { setup(); over(screen.getByLabelText('Close')); expect(screen.getByRole('tooltip')).toHaveTextContent('Close'); }); test('a title is shown in the app style and the browser\'s own tooltip is turned off', () => { setup(); const btn = screen.getByText('โ†'); over(btn); expect(screen.getByRole('tooltip')).toHaveTextContent('Out a level (Shift+Tab)'); expect(btn).not.toHaveAttribute('title'); expect(btn).toHaveAttribute('aria-label', 'Out a level (Shift+Tab)'); // a screen reader still gets its name }); test('a button that already says what it is gets no tooltip, and neither does a disabled one', () => { setup(); over(screen.getByText('Save changes')); expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); over(screen.getByLabelText('Nothing')); expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); }); test('clicking, scrolling or Escape dismisses it', () => { setup(); const btn = screen.getByLabelText('Search'); over(btn); act(() => { fireEvent.pointerDown(btn); }); expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); over(btn); act(() => { fireEvent.keyDown(document, { key: 'Escape' }); }); expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); over(btn); act(() => { fireEvent.scroll(window); }); expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); }); test('nothing shows on a device without hover (touch)', () => { vi.stubGlobal('matchMedia', () => ({ matches: false })); setup(); over(screen.getByLabelText('Search')); expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); vi.unstubAllGlobals(); }); test('tipFor ignores things that are not elements', () => { expect(tipFor(null)).toBe(''); expect(tipFor(document.createTextNode('x'))).toBe(''); });