Files
frontend-svelte/src/shared/lib/helpers/createPersistentStore/createPersistentStore.test.ts

378 lines
11 KiB
TypeScript
Raw Normal View History

/** @vitest-environment jsdom */
import {
afterEach,
beforeEach,
describe,
expect,
it,
vi,
} from 'vitest';
import { createPersistentStore } from './createPersistentStore.svelte';
describe('createPersistentStore', () => {
let mockLocalStorage: Storage;
const testKey = 'test-store-key';
beforeEach(() => {
// Mock localStorage
const storeMap = new Map<string, string>();
mockLocalStorage = {
get length() {
return storeMap.size;
},
clear() {
storeMap.clear();
},
getItem(key: string) {
return storeMap.get(key) ?? null;
},
setItem(key: string, value: string) {
storeMap.set(key, value);
},
removeItem(key: string) {
storeMap.delete(key);
},
key(index: number) {
return Array.from(storeMap.keys())[index] ?? null;
},
};
vi.stubGlobal('localStorage', mockLocalStorage);
});
afterEach(() => {
vi.unstubAllGlobals();
});
describe('Initialization', () => {
it('should create store with default value when localStorage is empty', () => {
const store = createPersistentStore(testKey, 'default');
expect(store.value).toBe('default');
});
it('should create store with value from localStorage', () => {
mockLocalStorage.setItem(testKey, JSON.stringify('stored value'));
const store = createPersistentStore(testKey, 'default');
expect(store.value).toBe('stored value');
});
it('should parse JSON from localStorage', () => {
const storedValue = { name: 'Test', count: 42 };
mockLocalStorage.setItem(testKey, JSON.stringify(storedValue));
const store = createPersistentStore(testKey, { name: 'Default', count: 0 });
expect(store.value).toEqual(storedValue);
});
it('should use default value when localStorage has invalid JSON', () => {
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
mockLocalStorage.setItem(testKey, 'invalid json{');
const store = createPersistentStore(testKey, 'default');
expect(store.value).toBe('default');
expect(consoleSpy).toHaveBeenCalled();
consoleSpy.mockRestore();
});
});
describe('Reading Values', () => {
it('should return current value via getter', () => {
const store = createPersistentStore(testKey, 'default');
expect(store.value).toBe('default');
});
it('should return updated value after setter', () => {
const store = createPersistentStore(testKey, 'default');
store.value = 'updated';
expect(store.value).toBe('updated');
});
it('should preserve type information', () => {
interface TestObject {
name: string;
count: number;
}
const defaultValue: TestObject = { name: 'Test', count: 0 };
const store = createPersistentStore<TestObject>(testKey, defaultValue);
expect(store.value.name).toBe('Test');
expect(store.value.count).toBe(0);
});
});
describe('Writing Values', () => {
it('should update value when set via setter', () => {
const store = createPersistentStore(testKey, 'default');
store.value = 'new value';
expect(store.value).toBe('new value');
});
it('should serialize objects to JSON', () => {
const store = createPersistentStore(testKey, { name: 'Default', count: 0 });
store.value = { name: 'Updated', count: 42 };
// The value is updated in the store
expect(store.value).toEqual({ name: 'Updated', count: 42 });
});
it('should handle arrays', () => {
const store = createPersistentStore<number[]>(testKey, []);
store.value = [1, 2, 3];
expect(store.value).toEqual([1, 2, 3]);
});
it('should handle booleans', () => {
const store = createPersistentStore<boolean>(testKey, false);
store.value = true;
expect(store.value).toBe(true);
});
it('should handle null values', () => {
const store = createPersistentStore<string | null>(testKey, null);
store.value = 'not null';
expect(store.value).toBe('not null');
});
});
describe('Clear Function', () => {
it('should reset value to default when clear is called', () => {
const store = createPersistentStore(testKey, 'default');
store.value = 'modified';
store.clear();
expect(store.value).toBe('default');
});
it('should work with object defaults', () => {
const defaultValue = { name: 'Default', count: 0 };
const store = createPersistentStore(testKey, defaultValue);
store.value = { name: 'Modified', count: 42 };
store.clear();
expect(store.value).toEqual(defaultValue);
});
it('should work with array defaults', () => {
const defaultValue = [1, 2, 3];
const store = createPersistentStore<number[]>(testKey, defaultValue);
store.value = [4, 5, 6];
store.clear();
expect(store.value).toEqual(defaultValue);
});
});
describe('Type Support', () => {
it('should work with string type', () => {
const store = createPersistentStore<string>(testKey, 'default');
store.value = 'test string';
expect(store.value).toBe('test string');
});
it('should work with number type', () => {
const store = createPersistentStore<number>(testKey, 0);
store.value = 42;
expect(store.value).toBe(42);
});
it('should work with boolean type', () => {
const store = createPersistentStore<boolean>(testKey, false);
store.value = true;
expect(store.value).toBe(true);
});
it('should work with object type', () => {
interface TestObject {
name: string;
value: number;
}
const defaultValue: TestObject = { name: 'Test', value: 0 };
const store = createPersistentStore<TestObject>(testKey, defaultValue);
store.value = { name: 'Updated', value: 42 };
expect(store.value.name).toBe('Updated');
expect(store.value.value).toBe(42);
});
it('should work with array type', () => {
const store = createPersistentStore<string[]>(testKey, []);
store.value = ['a', 'b', 'c'];
expect(store.value).toEqual(['a', 'b', 'c']);
});
it('should work with null type', () => {
const store = createPersistentStore<string | null>(testKey, null);
expect(store.value).toBeNull();
store.value = 'not null';
expect(store.value).toBe('not null');
});
});
describe('Edge Cases', () => {
it('should handle empty string', () => {
const store = createPersistentStore(testKey, 'default');
store.value = '';
expect(store.value).toBe('');
});
it('should handle zero number', () => {
const store = createPersistentStore<number>(testKey, 100);
store.value = 0;
expect(store.value).toBe(0);
});
it('should handle false boolean', () => {
const store = createPersistentStore<boolean>(testKey, true);
store.value = false;
expect(store.value).toBe(false);
});
it('should handle empty array', () => {
const store = createPersistentStore<number[]>(testKey, [1, 2, 3]);
store.value = [];
expect(store.value).toEqual([]);
});
it('should handle empty object', () => {
const store = createPersistentStore<Record<string, unknown>>(testKey, { a: 1 });
store.value = {};
expect(store.value).toEqual({});
});
it('should handle special characters in string', () => {
const store = createPersistentStore(testKey, '');
const specialString = 'Hello "world"\nNew line\tTab';
store.value = specialString;
expect(store.value).toBe(specialString);
});
it('should handle unicode characters', () => {
const store = createPersistentStore(testKey, '');
store.value = 'Hello 世界 🌍';
expect(store.value).toBe('Hello 世界 🌍');
});
});
describe('Multiple Instances', () => {
it('should handle multiple stores with different keys', () => {
const store1 = createPersistentStore('key1', 'value1');
const store2 = createPersistentStore('key2', 'value2');
store1.value = 'updated1';
store2.value = 'updated2';
expect(store1.value).toBe('updated1');
expect(store2.value).toBe('updated2');
});
it('should keep stores independent', () => {
const store1 = createPersistentStore('key1', 'default1');
const store2 = createPersistentStore('key2', 'default2');
store1.clear();
expect(store1.value).toBe('default1');
expect(store2.value).toBe('default2');
});
});
describe('Complex Scenarios', () => {
it('should handle nested objects', () => {
interface NestedObject {
user: {
name: string;
settings: {
theme: string;
notifications: boolean;
};
};
}
const defaultValue: NestedObject = {
user: {
name: 'Test',
settings: { theme: 'light', notifications: true },
},
};
const store = createPersistentStore<NestedObject>(testKey, defaultValue);
store.value = {
user: {
name: 'Updated',
settings: { theme: 'dark', notifications: false },
},
};
expect(store.value).toEqual({
user: {
name: 'Updated',
settings: { theme: 'dark', notifications: false },
},
});
});
it('should handle arrays of objects', () => {
interface Item {
id: number;
name: string;
}
const store = createPersistentStore<Item[]>(testKey, []);
store.value = [
{ id: 1, name: 'First' },
{ id: 2, name: 'Second' },
{ id: 3, name: 'Third' },
];
expect(store.value).toHaveLength(3);
expect(store.value[0].name).toBe('First');
});
});
});