feature/vitest-setup #13

Merged
ilia merged 4 commits from feature/vitest-setup into main 2026-01-06 09:24:58 +00:00
2 changed files with 393 additions and 0 deletions
Showing only changes of commit 943e6e77d3 - Show all commits

View File

@@ -0,0 +1,85 @@
import type { Property } from '$shared/store/createFilterStore';
import {
fireEvent,
render,
screen,
} from '@testing-library/svelte';
import {
beforeEach,
describe,
expect,
it,
vi,
} from 'vitest';
import CheckboxFilter from './CheckboxFilter.svelte';
describe('CheckboxFilter', () => {
const mockProperties: Property[] = [
{ id: '1', name: 'Sans-serif', selected: false },
{ id: '2', name: 'Serif', selected: true },
{ id: '3', name: 'Display', selected: false },
];
const mockOnPropertyToggle = vi.fn();
beforeEach(() => {
mockOnPropertyToggle.mockClear();
});
it('renders with correct label', () => {
render(CheckboxFilter, {
displayedLabel: 'Font Categories',
properties: mockProperties,
onPropertyToggle: mockOnPropertyToggle,
});
expect(screen.getByText('Font Categories')).toBeInTheDocument();
});
it('displays all properties as checkboxes', () => {
render(CheckboxFilter, {
displayedLabel: 'Categories',
properties: mockProperties,
onPropertyToggle: mockOnPropertyToggle,
});
expect(screen.getByLabelText('Sans-serif')).toBeInTheDocument();
expect(screen.getByLabelText('Serif')).toBeInTheDocument();
expect(screen.getByLabelText('Display')).toBeInTheDocument();
});
it('shows selected count badge when items selected', () => {
render(CheckboxFilter, {
displayedLabel: 'Categories',
properties: mockProperties,
onPropertyToggle: mockOnPropertyToggle,
});
expect(screen.getByText('1')).toBeInTheDocument();
});
it('does not show badge when no items selected', () => {
const allUnselected = mockProperties.map(p => ({ ...p, selected: false }));
render(CheckboxFilter, {
displayedLabel: 'Categories',
properties: allUnselected,
onPropertyToggle: mockOnPropertyToggle,
});
expect(screen.queryByText('0')).not.toBeInTheDocument();
});
it('calls onPropertyToggle when checkbox clicked', async () => {
render(CheckboxFilter, {
displayedLabel: 'Categories',
properties: mockProperties,
onPropertyToggle: mockOnPropertyToggle,
});
const checkbox = screen.getByLabelText('Sans-serif');
await checkbox.click();
expect(mockOnPropertyToggle).toHaveBeenCalledWith('1');
});
});

View File

@@ -0,0 +1,308 @@
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);
});
});