import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { act } from 'react'; import { createRoot, type Root } from 'react-dom/client'; import { usePanesStore } from '../stores/PanesStore'; import { PaneToggleStrip } from './PaneToggleStrip'; vi.mock('../modals/ModalCoordinator', () => ({ openModal: vi.fn() })); import { openModal } from '../modals/ModalCoordinator'; (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; let container: HTMLDivElement; let root: Root; const toggleButtons = () => Array.from(container.querySelectorAll('button[aria-pressed]')); const datasetsButton = () => container.querySelector('button[aria-label="Datasets"]')!; beforeEach(() => { usePanesStore.getState().hydrate({}, {}); container = document.createElement('div'); document.body.appendChild(container); root = createRoot(container); act(() => root.render()); }); afterEach(() => { act(() => root.unmount()); container.remove(); vi.clearAllMocks(); }); describe('PaneToggleStrip', () => { test('renders an APG toolbar: three pane toggles + a Datasets command button', () => { expect(container.querySelector('[role="toolbar"][aria-orientation="vertical"]')).not.toBeNull(); expect(toggleButtons()).toHaveLength(3); // The Datasets control is a command button, not a toggle (no aria-pressed). expect(datasetsButton().hasAttribute('aria-pressed')).toBe(false); }); test('aria-pressed mirrors pane visibility and flips on click', () => { const [library] = toggleButtons(); expect(library.getAttribute('aria-pressed')).toBe('true'); act(() => library.click()); expect(usePanesStore.getState().libraryVisible).toBe(false); expect(library.getAttribute('aria-pressed')).toBe('false'); }); test('the accessible name is stable across the pressed/not-pressed flip (APG)', () => { const [library] = toggleButtons(); expect(library.getAttribute('aria-label')).toBe('Library pane'); act(() => library.click()); expect(library.getAttribute('aria-label')).toBe('Library pane'); }); test('roving tabindex: exactly one control is in the tab order at a time', () => { const all = [...toggleButtons(), datasetsButton()]; expect(all.filter((b) => b.tabIndex === 0)).toHaveLength(1); expect(all[0].tabIndex).toBe(0); // first control by default }); test('ArrowDown moves the roving tab stop to the next control', () => { const all = [...toggleButtons(), datasetsButton()]; act(() => { all[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })); }); expect(all[0].tabIndex).toBe(-1); expect(all[1].tabIndex).toBe(0); }); test('End jumps to the last control (Datasets); Home returns to the first', () => { const all = [...toggleButtons(), datasetsButton()]; act(() => { all[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'End', bubbles: true })); }); expect(datasetsButton().tabIndex).toBe(0); act(() => { datasetsButton().dispatchEvent(new KeyboardEvent('keydown', { key: 'Home', bubbles: true })); }); expect(all[0].tabIndex).toBe(0); }); test('ArrowUp from the first control wraps to the last', () => { const all = [...toggleButtons(), datasetsButton()]; act(() => { all[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true })); }); expect(datasetsButton().tabIndex).toBe(0); }); test('the Datasets button opens the Datasets manager', () => { act(() => datasetsButton().click()); expect(openModal).toHaveBeenCalledWith('datasets'); }); });