kimai-plugin-heatmap/assets/test/controls.test.ts
Christopher Mühl 12e734a911
fix(07): uniform control sizing, min cell 13px, center week view
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 16:56:34 +02:00

111 lines
4.2 KiB
TypeScript

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 nav-sm');
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');
});
});