feat: create tests for shared/ui components
This commit is contained in:
85
src/shared/ui/CheckboxFilter/CheckboxFilter.svelte.test.ts
Normal file
85
src/shared/ui/CheckboxFilter/CheckboxFilter.svelte.test.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
308
src/shared/ui/ComboControl/ComboControl.svelte.test.ts
Normal file
308
src/shared/ui/ComboControl/ComboControl.svelte.test.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user