Files
study-bible-project/src/components/Tooltips.test.jsx
T
nmemmertandClaude Sonnet 5.5 7f0256be06 Hover tooltips, clickable cross-references, easier highlighting
- Tooltips on hover and keyboard focus across the app: icon-only buttons show
  their label, existing titles show in the app's style without the browser's
  delay, and the reader's main buttons show shortcut hints ("Search (/)").
  Touch screens are unaffected.
- Reader: cross-references (under verses and in the Cross-refs tab) open the
  passage's text in the side panel without leaving the chapter; "Open chapter"
  goes there when wanted
- Reader: the verse panel now has a Highlight row of colours for one-tap
  whole-verse highlighting (tap again to remove, Clear for all, with Undo);
  selecting words still highlights just those
- Fix: a cross-reference preview was cleared when React ran the panel's
  effect twice (development builds)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 16:31:27 -04:00

89 lines
3.2 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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(
<>
<button aria-label="Search" data-tip="Search (/)">🔍</button>
<button aria-label="Close">×</button>
<button title="Out a level (Shift+Tab)">←</button>
<button aria-label="Save changes">Save changes</button>
<button disabled aria-label="Nothing">·</button>
<Tooltips theme="paper" />
</>,
);
}
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('');
});