feat: test coverage for store creators
This commit is contained in:
@@ -0,0 +1,89 @@
|
|||||||
|
import { get } from 'svelte/store';
|
||||||
|
import {
|
||||||
|
beforeEach,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
it,
|
||||||
|
} from 'vitest';
|
||||||
|
import {
|
||||||
|
type ControlModel,
|
||||||
|
createControlStore,
|
||||||
|
} from './createControlStore';
|
||||||
|
|
||||||
|
describe('createControlStore', () => {
|
||||||
|
let store: ReturnType<typeof createControlStore<number>>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const initialState: ControlModel<number> = {
|
||||||
|
value: 10,
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
step: 5,
|
||||||
|
};
|
||||||
|
store = createControlStore(initialState);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('initializes with correct state', () => {
|
||||||
|
expect(get(store)).toEqual({
|
||||||
|
value: 10,
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
step: 5,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('increases value by step', () => {
|
||||||
|
store.increase();
|
||||||
|
expect(get(store).value).toBe(15);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('decreases value by step', () => {
|
||||||
|
store.decrease();
|
||||||
|
expect(get(store).value).toBe(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clamps value at maximum', () => {
|
||||||
|
store.setValue(200);
|
||||||
|
expect(get(store).value).toBe(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clamps value at minimum', () => {
|
||||||
|
store.setValue(-10);
|
||||||
|
expect(get(store).value).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rounds to step precision', () => {
|
||||||
|
store.setValue(12.34);
|
||||||
|
// With step=5, 12.34 is clamped and rounded to nearest integer (0 decimal places)
|
||||||
|
expect(get(store).value).toBe(12);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles decimal steps correctly', () => {
|
||||||
|
const decimalStore = createControlStore({
|
||||||
|
value: 1.0,
|
||||||
|
min: 0,
|
||||||
|
max: 2,
|
||||||
|
step: 0.05,
|
||||||
|
});
|
||||||
|
decimalStore.increase();
|
||||||
|
expect(get(decimalStore).value).toBe(1.05);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isAtMax returns true when at maximum', () => {
|
||||||
|
store.setValue(100);
|
||||||
|
expect(store.isAtMax()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isAtMax returns false when not at maximum', () => {
|
||||||
|
expect(store.isAtMax()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isAtMin returns true when at minimum', () => {
|
||||||
|
store.setValue(0);
|
||||||
|
expect(store.isAtMin()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isAtMin returns false when not at minimum', () => {
|
||||||
|
expect(store.isAtMin()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
136
src/shared/lib/store/createFilterStore/createFilterStore.test.ts
Normal file
136
src/shared/lib/store/createFilterStore/createFilterStore.test.ts
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import { get } from 'svelte/store';
|
||||||
|
import {
|
||||||
|
beforeEach,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
it,
|
||||||
|
} from 'vitest';
|
||||||
|
import {
|
||||||
|
type FilterModel,
|
||||||
|
type Property,
|
||||||
|
createFilterStore,
|
||||||
|
} from './createFilterStore';
|
||||||
|
|
||||||
|
describe('createFilterStore', () => {
|
||||||
|
const mockProperties: Property[] = [
|
||||||
|
{ id: '1', name: 'Sans-serif', selected: false },
|
||||||
|
{ id: '2', name: 'Serif', selected: false },
|
||||||
|
{ id: '3', name: 'Display', selected: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
let store: ReturnType<typeof createFilterStore>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const initialState: FilterModel = {
|
||||||
|
searchQuery: '',
|
||||||
|
properties: mockProperties,
|
||||||
|
};
|
||||||
|
store = createFilterStore(initialState);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('initializes with correct state', () => {
|
||||||
|
const state = get(store);
|
||||||
|
expect(state).toEqual({
|
||||||
|
searchQuery: '',
|
||||||
|
properties: mockProperties,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets search query', () => {
|
||||||
|
store.setSearchQuery('serif');
|
||||||
|
const state = get(store);
|
||||||
|
expect(state.searchQuery).toBe('serif');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clears search query', () => {
|
||||||
|
store.setSearchQuery('test');
|
||||||
|
store.clearSearchQuery();
|
||||||
|
const state = get(store);
|
||||||
|
expect(state.searchQuery).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('selects a property', () => {
|
||||||
|
store.selectProperty('1');
|
||||||
|
const state = get(store);
|
||||||
|
const property = state.properties.find(p => p.id === '1');
|
||||||
|
expect(property?.selected).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('deselects a property', () => {
|
||||||
|
store.selectProperty('1');
|
||||||
|
store.deselectProperty('1');
|
||||||
|
const state = get(store);
|
||||||
|
const property = state.properties.find(p => p.id === '1');
|
||||||
|
expect(property?.selected).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('toggles property from unselected to selected', () => {
|
||||||
|
store.toggleProperty('1');
|
||||||
|
const state = get(store);
|
||||||
|
const property = state.properties.find(p => p.id === '1');
|
||||||
|
expect(property?.selected).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('toggles property from selected to unselected', () => {
|
||||||
|
store.selectProperty('1');
|
||||||
|
store.toggleProperty('1');
|
||||||
|
const state = get(store);
|
||||||
|
const property = state.properties.find(p => p.id === '1');
|
||||||
|
expect(property?.selected).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('selects all properties', () => {
|
||||||
|
store.selectAllProperties();
|
||||||
|
const state = get(store);
|
||||||
|
expect(state.properties.every(p => p.selected)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('deselects all properties', () => {
|
||||||
|
store.selectAllProperties();
|
||||||
|
store.deselectAllProperties();
|
||||||
|
const state = get(store);
|
||||||
|
expect(state.properties.every(p => !p.selected)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets all properties', () => {
|
||||||
|
const allProps = store.getAllProperties();
|
||||||
|
const props = get(allProps);
|
||||||
|
expect(props).toEqual(mockProperties);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets selected properties', () => {
|
||||||
|
store.selectProperty('1');
|
||||||
|
store.selectProperty('3');
|
||||||
|
const selectedProps = store.getSelectedProperties();
|
||||||
|
const props = get(selectedProps);
|
||||||
|
expect(props).toHaveLength(2);
|
||||||
|
expect(props?.[0].id).toBe('1');
|
||||||
|
expect(props?.[1].id).toBe('3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filters properties by search query', () => {
|
||||||
|
store.setSearchQuery('serif');
|
||||||
|
const filteredProps = store.getFilteredProperties();
|
||||||
|
const props = get(filteredProps);
|
||||||
|
// 'serif' is a substring of 'Sans-serif' (case-sensitive match)
|
||||||
|
expect(props).toHaveLength(1);
|
||||||
|
expect(props?.[0].id).toBe('1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filter is case-sensitive', () => {
|
||||||
|
store.setSearchQuery('San');
|
||||||
|
const filteredProps = store.getFilteredProperties();
|
||||||
|
const props = get(filteredProps);
|
||||||
|
// 'San' matches 'Sans-serif' exactly (case-sensitive)
|
||||||
|
expect(props).toHaveLength(1);
|
||||||
|
expect(props?.[0].id).toBe('1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filter returns all properties when query is empty', () => {
|
||||||
|
store.setSearchQuery('');
|
||||||
|
const filteredProps = store.getFilteredProperties();
|
||||||
|
let props: Property[] | undefined = undefined;
|
||||||
|
filteredProps.subscribe(p => (props = p))();
|
||||||
|
expect(props).toHaveLength(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
18
src/shared/lib/store/index.ts
Normal file
18
src/shared/lib/store/index.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Shared store exports
|
||||||
|
*
|
||||||
|
* Exports all store creators and types for Svelte 5 reactive state management
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { createFilterStore } from './createFilterStore/createFilterStore';
|
||||||
|
export type {
|
||||||
|
FilterModel,
|
||||||
|
FilterStore,
|
||||||
|
Property,
|
||||||
|
} from './createFilterStore/createFilterStore';
|
||||||
|
|
||||||
|
export { createControlStore } from './createControlStore/createControlStore';
|
||||||
|
export type {
|
||||||
|
ControlModel,
|
||||||
|
ControlStoreModel,
|
||||||
|
} from './createControlStore/createControlStore';
|
||||||
Reference in New Issue
Block a user