feat: implement reactive BatchFontStore

This commit is contained in:
Ilia Mashkov
2026-04-15 12:25:49 +03:00
parent 4e9670118a
commit f4a568832a
2 changed files with 170 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
import { queryClient } from '$shared/api/queryClient';
import { fontKeys } from '$shared/api/queryKeys';
import {
beforeEach,
describe,
expect,
it,
vi,
} from 'vitest';
import * as api from '../../api/proxy/proxyFonts';
import { FontNetworkError } from '../../lib/errors/errors';
import { BatchFontStore } from './batchFontStore.svelte';
describe('BatchFontStore', () => {
beforeEach(() => {
queryClient.clear();
vi.clearAllMocks();
});
describe('Fetch Behavior', () => {
it('should skip fetch when initialized with empty IDs', async () => {
const spy = vi.spyOn(api, 'fetchFontsByIds');
const store = new BatchFontStore([]);
expect(spy).not.toHaveBeenCalled();
expect(store.fonts).toEqual([]);
});
it('should fetch and seed cache for valid IDs', async () => {
const fonts = [{ id: 'a', name: 'A' }] as any[];
vi.spyOn(api, 'fetchFontsByIds').mockResolvedValue(fonts);
const store = new BatchFontStore(['a']);
await vi.waitFor(() => expect(store.fonts).toEqual(fonts), { timeout: 1000 });
expect(queryClient.getQueryData(fontKeys.detail('a'))).toEqual(fonts[0]);
});
});
describe('Loading States', () => {
it('should transition through loading state', async () => {
vi.spyOn(api, 'fetchFontsByIds').mockImplementation(() =>
new Promise(r => setTimeout(() => r([{ id: 'a' }] as any), 50))
);
const store = new BatchFontStore(['a']);
expect(store.isLoading).toBe(true);
await vi.waitFor(() => expect(store.isLoading).toBe(false), { timeout: 1000 });
});
});
describe('Error Handling', () => {
it('should wrap network failures in FontNetworkError', async () => {
vi.spyOn(api, 'fetchFontsByIds').mockRejectedValue(new Error('Network fail'));
const store = new BatchFontStore(['a']);
await vi.waitFor(() => expect(store.isError).toBe(true), { timeout: 1000 });
expect(store.error).toBeInstanceOf(FontNetworkError);
});
it('should handle malformed API responses with FontResponseError', async () => {
// Mocking a malformed response that the store should validate
vi.spyOn(api, 'fetchFontsByIds').mockResolvedValue(null as any);
const store = new BatchFontStore(['a']);
await vi.waitFor(() => expect(store.isError).toBe(true), { timeout: 1000 });
});
});
describe('Reactivity', () => {
it('should refetch when setIds is called', async () => {
const fonts1 = [{ id: 'a' }] as any[];
const fonts2 = [{ id: 'b' }] as any[];
vi.spyOn(api, 'fetchFontsByIds')
.mockResolvedValueOnce(fonts1)
.mockResolvedValueOnce(fonts2);
const store = new BatchFontStore(['a']);
await vi.waitFor(() => expect(store.fonts).toEqual(fonts1), { timeout: 1000 });
store.setIds(['b']);
await vi.waitFor(() => expect(store.fonts).toEqual(fonts2), { timeout: 1000 });
});
});
});