import React from 'react'; import { describe, it, expect, vi } from 'vitest'; import { render, fireEvent, act } from '@testing-library/react'; import { PixelCommand } from '../../overlays/PixelCommand'; function makeGroups(onA = vi.fn(), onB = vi.fn(), onC = vi.fn()) { return [ { heading: 'Navigation', items: [ { id: 'home', label: 'Go home', keywords: ['dashboard'], onSelect: onA }, { id: 'settings', label: 'Open settings', keywords: ['prefs'], onSelect: onB }, ], }, { heading: 'Actions', items: [ { id: 'logout', label: 'Log out', shortcut: 'mod+q', onSelect: onC }, ], }, ]; } describe('PixelCommand', () => { it('renders nothing when open=false', () => { const onOpenChange = vi.fn(); const { queryByRole } = render( , ); expect(queryByRole('combobox')).toBeNull(); expect(queryByRole('listbox')).toBeNull(); }); it('renders search input and groups when open=true', () => { const onOpenChange = vi.fn(); const { getByRole, getByText, getAllByRole } = render( , ); const input = getByRole('combobox') as HTMLInputElement; expect(input).toBeTruthy(); expect(input.placeholder).toBe('Search…'); expect(getByRole('listbox')).toBeTruthy(); expect(getByText('Navigation')).toBeTruthy(); expect(getByText('Actions')).toBeTruthy(); expect(getByText('Go home')).toBeTruthy(); expect(getByText('Open settings')).toBeTruthy(); expect(getByText('Log out')).toBeTruthy(); expect(getAllByRole('option').length).toBe(3); }); it('typing filters items by label substring', () => { const onOpenChange = vi.fn(); const { getByRole, queryByText, getByText } = render( , ); const input = getByRole('combobox') as HTMLInputElement; fireEvent.change(input, { target: { value: 'home' } }); expect(getByText('Go home')).toBeTruthy(); expect(queryByText('Open settings')).toBeNull(); expect(queryByText('Log out')).toBeNull(); }); it('typing matches against keywords too', () => { const onOpenChange = vi.fn(); const { getByRole, queryByText, getByText } = render( , ); const input = getByRole('combobox') as HTMLInputElement; fireEvent.change(input, { target: { value: 'prefs' } }); expect(getByText('Open settings')).toBeTruthy(); expect(queryByText('Go home')).toBeNull(); }); it('shows emptyMessage when no items match', () => { const onOpenChange = vi.fn(); const { getByRole, getByText } = render( , ); const input = getByRole('combobox') as HTMLInputElement; fireEvent.change(input, { target: { value: 'zzzzzzzz' } }); expect(getByText('Nothing found')).toBeTruthy(); }); it('arrow down highlights next item (aria-selected)', () => { const onOpenChange = vi.fn(); const { getByRole, getAllByRole } = render( , ); const input = getByRole('combobox') as HTMLInputElement; // Initial: first option is highlighted by default. let options = getAllByRole('option'); expect(options[0].getAttribute('aria-selected')).toBe('true'); fireEvent.keyDown(input, { key: 'ArrowDown' }); options = getAllByRole('option'); expect(options[1].getAttribute('aria-selected')).toBe('true'); expect(options[0].getAttribute('aria-selected')).toBe('false'); }); it('Enter calls onSelect of highlighted item', () => { const onA = vi.fn(); const onB = vi.fn(); const onC = vi.fn(); const onOpenChange = vi.fn(); const { getByRole } = render( , ); const input = getByRole('combobox') as HTMLInputElement; fireEvent.keyDown(input, { key: 'ArrowDown' }); fireEvent.keyDown(input, { key: 'Enter' }); expect(onB).toHaveBeenCalled(); expect(onA).not.toHaveBeenCalled(); expect(onC).not.toHaveBeenCalled(); }); it('Escape calls onOpenChange(false)', () => { const onOpenChange = vi.fn(); render( , ); act(() => { window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })); }); expect(onOpenChange).toHaveBeenCalledWith(false); }); it('keyboard shortcut (mod+k) toggles open', () => { const onOpenChange = vi.fn(); render( , ); act(() => { window.dispatchEvent( new KeyboardEvent('keydown', { key: 'k', metaKey: true, bubbles: true }), ); }); expect(onOpenChange).toHaveBeenCalledWith(true); }); it('keyboard shortcut (mod+k) also fires with ctrlKey', () => { const onOpenChange = vi.fn(); render( , ); act(() => { window.dispatchEvent( new KeyboardEvent('keydown', { key: 'k', ctrlKey: true, bubbles: true }), ); }); expect(onOpenChange).toHaveBeenCalledWith(true); }); it('clicking an option calls its onSelect', () => { const onA = vi.fn(); const onOpenChange = vi.fn(); const { getByText } = render( , ); fireEvent.click(getByText('Go home')); expect(onA).toHaveBeenCalled(); }); });