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