import { describe, it, expect, vi } from 'vitest'; import { createModeControl, createMetricControl } from '../src/ui/controls'; describe('createModeControl', () => { const modes = [ { key: 'year', label: 'Year' }, { key: 'week', label: 'Week' }, ]; it('returns a nav element with nav-segmented class and tablist role', () => { const nav = createModeControl('year', modes, () => {}); expect(nav.tagName).toBe('NAV'); expect(nav.className).toBe('nav nav-segmented'); expect(nav.getAttribute('role')).toBe('tablist'); }); it('renders buttons for each mode', () => { const nav = createModeControl('year', modes, () => {}); const buttons = nav.querySelectorAll('button'); expect(buttons.length).toBe(2); expect(buttons[0].textContent).toBe('Year'); expect(buttons[1].textContent).toBe('Week'); }); it('marks the active mode with active class and aria-selected', () => { const nav = createModeControl('year', modes, () => {}); const buttons = nav.querySelectorAll('button'); expect(buttons[0].classList.contains('active')).toBe(true); expect(buttons[0].getAttribute('aria-selected')).toBe('true'); expect(buttons[1].classList.contains('active')).toBe(false); expect(buttons[1].getAttribute('aria-selected')).toBe('false'); }); it('all buttons have nav-link class and role=tab', () => { const nav = createModeControl('year', modes, () => {}); const buttons = nav.querySelectorAll('button'); buttons.forEach((btn) => { expect(btn.classList.contains('nav-link')).toBe(true); expect(btn.getAttribute('role')).toBe('tab'); }); }); it('clicking inactive button updates active state and calls onChange', () => { const onChange = vi.fn(); const nav = createModeControl('year', modes, onChange); const buttons = nav.querySelectorAll('button'); buttons[1].click(); expect(onChange).toHaveBeenCalledWith('week'); expect(buttons[1].classList.contains('active')).toBe(true); expect(buttons[1].getAttribute('aria-selected')).toBe('true'); expect(buttons[0].classList.contains('active')).toBe(false); expect(buttons[0].getAttribute('aria-selected')).toBe('false'); }); it('clicking already active button still calls onChange', () => { const onChange = vi.fn(); const nav = createModeControl('year', modes, onChange); const buttons = nav.querySelectorAll('button'); buttons[0].click(); expect(onChange).toHaveBeenCalledWith('year'); }); }); describe('createMetricControl', () => { it('returns a nav element with nav-segmented nav-sm classes', () => { const nav = createMetricControl('hours', () => {}); expect(nav.tagName).toBe('NAV'); expect(nav.className).toBe('nav nav-segmented nav-sm'); expect(nav.getAttribute('role')).toBe('tablist'); }); it('renders Hours and Count buttons', () => { const nav = createMetricControl('hours', () => {}); const buttons = nav.querySelectorAll('button'); expect(buttons.length).toBe(2); expect(buttons[0].textContent).toBe('Hours'); expect(buttons[1].textContent).toBe('Count'); }); it('marks hours as active by default', () => { const nav = createMetricControl('hours', () => {}); const buttons = nav.querySelectorAll('button'); expect(buttons[0].classList.contains('active')).toBe(true); expect(buttons[1].classList.contains('active')).toBe(false); }); it('clicking Count calls onChange with count', () => { const onChange = vi.fn(); const nav = createMetricControl('hours', onChange); const buttons = nav.querySelectorAll('button'); buttons[1].click(); expect(onChange).toHaveBeenCalledWith('count'); expect(buttons[1].classList.contains('active')).toBe(true); expect(buttons[0].classList.contains('active')).toBe(false); }); it('buttons have proper ARIA attributes', () => { const nav = createMetricControl('count', () => {}); const buttons = nav.querySelectorAll('button'); expect(buttons[0].getAttribute('role')).toBe('tab'); expect(buttons[0].getAttribute('aria-selected')).toBe('false'); expect(buttons[1].getAttribute('role')).toBe('tab'); expect(buttons[1].getAttribute('aria-selected')).toBe('true'); }); });