Files
frontend-svelte/src/shared/ui/ComboControl/ComboControl.svelte.test.ts

309 lines
10 KiB
TypeScript
Raw Normal View History

import {
fireEvent,
render,
} from '@testing-library/svelte';
import {
beforeEach,
describe,
expect,
it,
vi,
} from 'vitest';
import ComboControl from './ComboControl.svelte';
describe('ComboControl', () => {
const onChangeMock = vi.fn() as (value: number) => void;
const onIncreaseMock = vi.fn() as () => void;
const onDecreaseMock = vi.fn() as () => void;
it('renders with default values', () => {
const { container } = render(ComboControl, {
value: 50,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
});
// Check that the control button displays the value
const controlButton = container.querySelector(
'button[variant="outline"][size="icon"]:nth-child(2)',
);
expect(controlButton?.textContent).toBe('50');
});
it('renders with custom min/max/step', () => {
const { container } = render(ComboControl, {
value: 5,
minValue: 0,
maxValue: 10,
step: 0.5,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
});
const controlButton = container.querySelector(
'button[variant="outline"][size="icon"]:nth-child(2)',
);
expect(controlButton?.textContent).toBe('5');
});
it('calls onIncrease when increase button is clicked', async () => {
const { getByLabelText } = render(ComboControl, {
value: 5,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
increaseLabel: 'Increase value',
});
const increaseButton = getByLabelText('Increase value');
await fireEvent.click(increaseButton);
expect(onIncreaseMock).toHaveBeenCalledTimes(1);
});
it('calls onDecrease when decrease button is clicked', async () => {
const { getByLabelText } = render(ComboControl, {
value: 5,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
decreaseLabel: 'Decrease value',
});
const decreaseButton = getByLabelText('Decrease value');
await fireEvent.click(decreaseButton);
expect(onDecreaseMock).toHaveBeenCalledTimes(1);
});
it('disables increase button when increaseDisabled is true', () => {
const { getByLabelText } = render(ComboControl, {
value: 100,
minValue: 0,
maxValue: 100,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
increaseDisabled: true,
increaseLabel: 'Increase value',
});
const increaseButton = getByLabelText('Increase value');
expect(increaseButton).toBeDisabled();
});
it('disables decrease button when decreaseDisabled is true', () => {
const { getByLabelText } = render(ComboControl, {
value: 0,
minValue: 0,
maxValue: 100,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
decreaseDisabled: true,
decreaseLabel: 'Decrease value',
});
const decreaseButton = getByLabelText('Decrease value');
expect(decreaseButton).toBeDisabled();
});
it('opens popover when control button is clicked', async () => {
const { getByLabelText, queryByRole } = render(ComboControl, {
value: 5,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
controlLabel: 'Control value',
});
// Initially, popover content should not be visible
expect(queryByRole('dialog')).not.toBeInTheDocument();
const controlButton = getByLabelText('Control value');
await fireEvent.click(controlButton);
// After clicking, popover content should be visible
expect(queryByRole('dialog')).toBeInTheDocument();
});
it('updates value when slider changes', async () => {
const { getByLabelText, container } = render(ComboControl, {
value: 5,
minValue: 0,
maxValue: 10,
step: 1,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
controlLabel: 'Control value',
});
// Open popover
const controlButton = getByLabelText('Control value');
await fireEvent.click(controlButton);
// Find slider - the Slider component should render an input with role slider
const slider = container.querySelector('[role="slider"]');
expect(slider).toBeInTheDocument();
// Simulate slider change
await fireEvent.input(slider!, { target: { value: '7' } });
expect(onChangeMock).toHaveBeenCalledWith(7);
});
it('updates value when number input changes', async () => {
const { getByLabelText, container } = render(ComboControl, {
value: 5,
minValue: 0,
maxValue: 10,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
controlLabel: 'Control value',
});
// Open popover
const controlButton = getByLabelText('Control value');
await fireEvent.click(controlButton);
// Find number input
const input = container.querySelector('input[type="text"], input[type="number"]');
expect(input).toBeInTheDocument();
// Simulate input change
await fireEvent.change(input!, { target: { value: '8' } });
expect(onChangeMock).toHaveBeenCalledWith(8);
});
it('respects min and max values on input', () => {
const { getByLabelText, container } = render(ComboControl, {
value: 5,
minValue: 0,
maxValue: 10,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
controlLabel: 'Control value',
});
// Open popover
const controlButton = getByLabelText('Control value');
fireEvent.click(controlButton);
// Find input
const input = container.querySelector('input[type="text"], input[type="number"]');
// Check min and max attributes
expect(input).toHaveAttribute('min', '0');
expect(input).toHaveAttribute('max', '10');
});
it('uses custom aria-labels', () => {
const { getByLabelText } = render(ComboControl, {
value: 5,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
increaseLabel: 'Increase by step',
decreaseLabel: 'Decrease by step',
controlLabel: 'Change value',
});
expect(getByLabelText('Increase by step')).toBeInTheDocument();
expect(getByLabelText('Decrease by step')).toBeInTheDocument();
expect(getByLabelText('Change value')).toBeInTheDocument();
});
it('uses default min/max/step values when not provided', () => {
const { getByLabelText, container } = render(ComboControl, {
value: 50,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
controlLabel: 'Control value',
});
// Open popover
const controlButton = getByLabelText('Control value');
fireEvent.click(controlButton);
// Find input
const input = container.querySelector('input[type="text"], input[type="number"]');
// Check default values (0, 100, 1)
expect(input).toHaveAttribute('min', '0');
expect(input).toHaveAttribute('max', '100');
});
it('does not call onChange when input value is invalid', async () => {
const { getByLabelText, container } = render(ComboControl, {
value: 5,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
controlLabel: 'Control value',
});
// Open popover
const controlButton = getByLabelText('Control value');
await fireEvent.click(controlButton);
// Find input
const input = container.querySelector('input[type="text"], input[type="number"]');
// Simulate invalid input
await fireEvent.change(input!, { target: { value: 'invalid' } });
expect(onChangeMock).not.toHaveBeenCalled();
});
it('displays current value in input field', async () => {
const { getByLabelText, container } = render(ComboControl, {
value: 42,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
controlLabel: 'Control value',
});
// Open popover
const controlButton = getByLabelText('Control value');
await fireEvent.click(controlButton);
// Find input
const input = container.querySelector('input[type="text"], input[type="number"]');
expect(input).toHaveValue('42');
});
it('handles step value for slider precision', async () => {
const { getByLabelText, container } = render(ComboControl, {
value: 5,
minValue: 0,
maxValue: 10,
step: 0.25,
onChange: onChangeMock,
onIncrease: onIncreaseMock,
onDecrease: onDecreaseMock,
controlLabel: 'Control value',
});
// Open popover
const controlButton = getByLabelText('Control value');
await fireEvent.click(controlButton);
// Find slider
const slider = container.querySelector('[role="slider"]');
// Simulate slider change
await fireEvent.input(slider!, { target: { value: '5.5' } });
expect(onChangeMock).toHaveBeenCalledWith(5.5);
});
});