mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 17:41:09 +02:00
feat(shared-stores): add createViewStore factory for view/filter/sort
Generic factory that eliminates ~110 LOC boilerplate per module for view mode, sort, filters, and saved filter presets with localStorage persistence. Includes 23 tests covering all store operations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
de8335277a
commit
934f3337e3
7 changed files with 548 additions and 3 deletions
|
|
@ -9,11 +9,16 @@
|
|||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"type-check": "echo 'Skipping: shared-stores uses Svelte 5 runes, type-checked at build time'"
|
||||
"type-check": "echo 'Skipping: shared-stores uses Svelte 5 runes, type-checked at build time'",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||
"jsdom": "^29.0.1",
|
||||
"svelte": "^5.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
"typescript": "^5.0.0",
|
||||
"vitest": "^4.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@manacore/local-store": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,13 @@ export {
|
|||
type AppSettingsStore,
|
||||
type AppSettingsStoreOptions,
|
||||
} from './settings.svelte';
|
||||
export {
|
||||
createViewStore,
|
||||
type ViewStore,
|
||||
type ViewStoreConfig,
|
||||
type SortOption,
|
||||
type SavedFilter,
|
||||
} from './view.svelte';
|
||||
export {
|
||||
createSimpleNavigationStores,
|
||||
type SimpleNavigationStores,
|
||||
|
|
|
|||
5
packages/shared-stores/src/test/mock-environment.ts
Normal file
5
packages/shared-stores/src/test/mock-environment.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/** Mock for $app/environment — always acts as browser in tests */
|
||||
export const browser = true;
|
||||
export const building = false;
|
||||
export const dev = true;
|
||||
export const version = 'test';
|
||||
335
packages/shared-stores/src/view.svelte.test.ts
Normal file
335
packages/shared-stores/src/view.svelte.test.ts
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
|
||||
// Must dynamically import to get fresh $state per test
|
||||
async function freshStore() {
|
||||
vi.resetModules();
|
||||
const mod = await import('./view.svelte');
|
||||
return mod.createViewStore;
|
||||
}
|
||||
|
||||
type TestViewMode = 'list' | 'grid' | 'table';
|
||||
|
||||
interface TestFilters {
|
||||
search?: string;
|
||||
tagIds?: string[];
|
||||
status?: string[];
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
describe('createViewStore', () => {
|
||||
describe('defaults', () => {
|
||||
it('initializes with default view mode', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
expect(store.viewMode).toBe('list');
|
||||
});
|
||||
|
||||
it('initializes with default sort', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
expect(store.sort).toEqual({ field: 'name', direction: 'asc' });
|
||||
});
|
||||
|
||||
it('starts with empty filters', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
expect(store.activeFilters).toEqual({});
|
||||
});
|
||||
|
||||
it('starts with empty saved filters', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
expect(store.savedFilters).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('viewMode', () => {
|
||||
it('updates view mode', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.setViewMode('grid');
|
||||
expect(store.viewMode).toBe('grid');
|
||||
});
|
||||
|
||||
it('persists view mode to localStorage', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.setViewMode('table');
|
||||
expect(JSON.parse(localStorage.getItem('test_view_mode')!)).toBe('table');
|
||||
});
|
||||
});
|
||||
|
||||
describe('sort', () => {
|
||||
it('updates sort', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.setSort({ field: 'date', direction: 'desc' });
|
||||
expect(store.sort).toEqual({ field: 'date', direction: 'desc' });
|
||||
});
|
||||
|
||||
it('persists sort to localStorage', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.setSort({ field: 'date', direction: 'desc' });
|
||||
expect(JSON.parse(localStorage.getItem('test_sort')!)).toEqual({
|
||||
field: 'date',
|
||||
direction: 'desc',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('filters', () => {
|
||||
it('sets filters', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.setFilters({ search: 'hello', tagIds: ['t1'] });
|
||||
expect(store.activeFilters).toEqual({ search: 'hello', tagIds: ['t1'] });
|
||||
});
|
||||
|
||||
it('updates a single filter key', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.setFilters({ search: 'hello' });
|
||||
store.updateFilter('tagIds', ['t1', 't2']);
|
||||
expect(store.activeFilters).toEqual({ search: 'hello', tagIds: ['t1', 't2'] });
|
||||
});
|
||||
|
||||
it('clears all filters', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.setFilters({ search: 'hello', tagIds: ['t1'] });
|
||||
store.clearFilters();
|
||||
expect(store.activeFilters).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasActiveFilters', () => {
|
||||
it('returns false when no filters are set', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
expect(store.hasActiveFilters).toBe(false);
|
||||
});
|
||||
|
||||
it('uses default heuristic when no custom fn provided', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.setFilters({ search: 'hello' });
|
||||
expect(store.hasActiveFilters).toBe(true);
|
||||
});
|
||||
|
||||
it('ignores empty arrays in default heuristic', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.setFilters({ tagIds: [] });
|
||||
expect(store.hasActiveFilters).toBe(false);
|
||||
});
|
||||
|
||||
it('uses custom hasActiveFilters function', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
hasActiveFilters: (f) => !!f.search,
|
||||
});
|
||||
store.setFilters({ tagIds: ['t1'] });
|
||||
expect(store.hasActiveFilters).toBe(false);
|
||||
store.setFilters({ search: 'x' });
|
||||
expect(store.hasActiveFilters).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('saved filters', () => {
|
||||
it('saves a named filter preset', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.setFilters({ search: 'hello' });
|
||||
store.saveFilter('My Filter');
|
||||
expect(store.savedFilters).toHaveLength(1);
|
||||
expect(store.savedFilters[0].name).toBe('My Filter');
|
||||
expect(store.savedFilters[0].criteria).toEqual({ search: 'hello' });
|
||||
});
|
||||
|
||||
it('persists saved filters to localStorage', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.setFilters({ search: 'hello' });
|
||||
store.saveFilter('My Filter');
|
||||
const stored = JSON.parse(localStorage.getItem('test_saved_filters')!);
|
||||
expect(stored).toHaveLength(1);
|
||||
expect(stored[0].name).toBe('My Filter');
|
||||
});
|
||||
|
||||
it('loads a saved filter', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.setFilters({ search: 'hello' });
|
||||
store.saveFilter('My Filter');
|
||||
store.clearFilters();
|
||||
expect(store.activeFilters).toEqual({});
|
||||
store.loadFilter(store.savedFilters[0].id);
|
||||
expect(store.activeFilters).toEqual({ search: 'hello' });
|
||||
});
|
||||
|
||||
it('deletes a saved filter', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.setFilters({ search: 'hello' });
|
||||
store.saveFilter('Filter 1');
|
||||
store.saveFilter('Filter 2');
|
||||
expect(store.savedFilters).toHaveLength(2);
|
||||
store.deleteSavedFilter(store.savedFilters[0].id);
|
||||
expect(store.savedFilters).toHaveLength(1);
|
||||
expect(store.savedFilters[0].name).toBe('Filter 2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('initialize', () => {
|
||||
it('loads persisted view mode from localStorage', async () => {
|
||||
localStorage.setItem('test_view_mode', JSON.stringify('grid'));
|
||||
localStorage.setItem('test_sort', JSON.stringify({ field: 'date', direction: 'desc' }));
|
||||
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.initialize();
|
||||
expect(store.viewMode).toBe('grid');
|
||||
expect(store.sort).toEqual({ field: 'date', direction: 'desc' });
|
||||
});
|
||||
|
||||
it('only initializes once', async () => {
|
||||
localStorage.setItem('test_view_mode', JSON.stringify('grid'));
|
||||
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.initialize();
|
||||
expect(store.viewMode).toBe('grid');
|
||||
|
||||
// Change localStorage directly — second initialize should be no-op
|
||||
localStorage.setItem('test_view_mode', JSON.stringify('table'));
|
||||
store.initialize();
|
||||
expect(store.viewMode).toBe('grid');
|
||||
});
|
||||
|
||||
it('loads persisted saved filters', async () => {
|
||||
localStorage.setItem(
|
||||
'test_saved_filters',
|
||||
JSON.stringify([
|
||||
{ id: 'f1', name: 'Preset', criteria: { search: 'test' }, createdAt: '2024-01-01' },
|
||||
])
|
||||
);
|
||||
|
||||
const createViewStore = await freshStore();
|
||||
const store = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'test',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
store.initialize();
|
||||
expect(store.savedFilters).toHaveLength(1);
|
||||
expect(store.savedFilters[0].name).toBe('Preset');
|
||||
});
|
||||
});
|
||||
|
||||
describe('storage prefix isolation', () => {
|
||||
it('uses different localStorage keys per prefix', async () => {
|
||||
const createViewStore = await freshStore();
|
||||
const store1 = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'app1',
|
||||
defaultViewMode: 'list',
|
||||
defaultSort: { field: 'name', direction: 'asc' },
|
||||
});
|
||||
const _store2 = createViewStore<TestViewMode, TestFilters>({
|
||||
storagePrefix: 'app2',
|
||||
defaultViewMode: 'grid',
|
||||
defaultSort: { field: 'date', direction: 'desc' },
|
||||
});
|
||||
|
||||
store1.setViewMode('table');
|
||||
expect(JSON.parse(localStorage.getItem('app1_view_mode')!)).toBe('table');
|
||||
expect(localStorage.getItem('app2_view_mode')).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
176
packages/shared-stores/src/view.svelte.ts
Normal file
176
packages/shared-stores/src/view.svelte.ts
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
/**
|
||||
* View Store Factory
|
||||
*
|
||||
* Creates a type-safe view/filter/sort store with localStorage persistence.
|
||||
* Replaces ~110 LOC boilerplate per module.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { createViewStore } from '@manacore/shared-stores';
|
||||
*
|
||||
* type MyViewMode = 'list' | 'grid' | 'kanban';
|
||||
*
|
||||
* interface MyFilters {
|
||||
* search?: string;
|
||||
* status?: string[];
|
||||
* tagIds?: string[];
|
||||
* }
|
||||
*
|
||||
* export const viewStore = createViewStore<MyViewMode, MyFilters>({
|
||||
* storagePrefix: 'inventar',
|
||||
* defaultViewMode: 'list',
|
||||
* defaultSort: { field: 'name', direction: 'asc' },
|
||||
* hasActiveFilters: (f) => !!(f.search || f.status?.length || f.tagIds?.length),
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
export interface SortOption {
|
||||
field: string;
|
||||
direction: 'asc' | 'desc';
|
||||
}
|
||||
|
||||
export interface SavedFilter<F> {
|
||||
id: string;
|
||||
name: string;
|
||||
criteria: F;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface ViewStoreConfig<V extends string, F extends Record<string, unknown>> {
|
||||
/** Prefix for localStorage keys (e.g. 'inventar' → 'inventar_view_mode') */
|
||||
storagePrefix: string;
|
||||
/** Default view mode */
|
||||
defaultViewMode: V;
|
||||
/** Default sort option */
|
||||
defaultSort: SortOption;
|
||||
/** Returns true if any filters are active (used for UI indicators) */
|
||||
hasActiveFilters?: (filters: F) => boolean;
|
||||
}
|
||||
|
||||
export interface ViewStore<V extends string, F extends Record<string, unknown>> {
|
||||
readonly viewMode: V;
|
||||
readonly sort: SortOption;
|
||||
readonly activeFilters: F;
|
||||
readonly savedFilters: SavedFilter<F>[];
|
||||
readonly hasActiveFilters: boolean;
|
||||
|
||||
initialize(): void;
|
||||
setViewMode(mode: V): void;
|
||||
setSort(newSort: SortOption): void;
|
||||
setFilters(filters: F): void;
|
||||
updateFilter<K extends keyof F>(key: K, value: F[K]): void;
|
||||
clearFilters(): void;
|
||||
saveFilter(name: string): void;
|
||||
loadFilter(id: string): void;
|
||||
deleteSavedFilter(id: string): void;
|
||||
}
|
||||
|
||||
function load<T>(key: string, fallback: T): T {
|
||||
if (!browser) return fallback;
|
||||
try {
|
||||
const data = localStorage.getItem(key);
|
||||
return data ? JSON.parse(data) : fallback;
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
function save(key: string, value: unknown) {
|
||||
if (!browser) return;
|
||||
try {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
} catch {
|
||||
// Silently fail (quota exceeded, private mode, etc.)
|
||||
}
|
||||
}
|
||||
|
||||
export function createViewStore<V extends string, F extends Record<string, unknown>>(
|
||||
config: ViewStoreConfig<V, F>
|
||||
): ViewStore<V, F> {
|
||||
const VIEW_KEY = `${config.storagePrefix}_view_mode`;
|
||||
const SORT_KEY = `${config.storagePrefix}_sort`;
|
||||
const FILTERS_KEY = `${config.storagePrefix}_saved_filters`;
|
||||
|
||||
let viewMode = $state<V>(config.defaultViewMode);
|
||||
let sort = $state<SortOption>(config.defaultSort);
|
||||
let activeFilters = $state<F>({} as F);
|
||||
let savedFilters = $state<SavedFilter<F>[]>([]);
|
||||
let initialized = $state(false);
|
||||
|
||||
return {
|
||||
get viewMode() {
|
||||
return viewMode;
|
||||
},
|
||||
get sort() {
|
||||
return sort;
|
||||
},
|
||||
get activeFilters() {
|
||||
return activeFilters;
|
||||
},
|
||||
get savedFilters() {
|
||||
return savedFilters;
|
||||
},
|
||||
get hasActiveFilters() {
|
||||
if (config.hasActiveFilters) return config.hasActiveFilters(activeFilters);
|
||||
return Object.values(activeFilters).some(
|
||||
(v) => v !== undefined && v !== null && v !== '' && (!Array.isArray(v) || v.length > 0)
|
||||
);
|
||||
},
|
||||
|
||||
initialize() {
|
||||
if (initialized) return;
|
||||
viewMode = load<V>(VIEW_KEY, config.defaultViewMode);
|
||||
sort = load<SortOption>(SORT_KEY, config.defaultSort);
|
||||
savedFilters = load<SavedFilter<F>[]>(FILTERS_KEY, []);
|
||||
initialized = true;
|
||||
},
|
||||
|
||||
setViewMode(mode: V) {
|
||||
viewMode = mode;
|
||||
save(VIEW_KEY, mode);
|
||||
},
|
||||
|
||||
setSort(newSort: SortOption) {
|
||||
sort = newSort;
|
||||
save(SORT_KEY, newSort);
|
||||
},
|
||||
|
||||
setFilters(filters: F) {
|
||||
activeFilters = filters;
|
||||
},
|
||||
|
||||
updateFilter<K extends keyof F>(key: K, value: F[K]) {
|
||||
activeFilters = { ...activeFilters, [key]: value };
|
||||
},
|
||||
|
||||
clearFilters() {
|
||||
activeFilters = {} as F;
|
||||
},
|
||||
|
||||
saveFilter(name: string) {
|
||||
const filter: SavedFilter<F> = {
|
||||
id: crypto.randomUUID(),
|
||||
name,
|
||||
criteria: { ...activeFilters },
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
savedFilters = [...savedFilters, filter];
|
||||
save(FILTERS_KEY, savedFilters);
|
||||
},
|
||||
|
||||
loadFilter(id: string) {
|
||||
const filter = savedFilters.find((f) => f.id === id);
|
||||
if (filter) {
|
||||
activeFilters = { ...filter.criteria };
|
||||
}
|
||||
},
|
||||
|
||||
deleteSavedFilter(id: string) {
|
||||
savedFilters = savedFilters.filter((f) => f.id !== id);
|
||||
save(FILTERS_KEY, savedFilters);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -11,6 +11,6 @@
|
|||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"include": ["src/**/*", "vitest.config.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
|
|
|||
17
packages/shared-stores/vitest.config.ts
Normal file
17
packages/shared-stores/vitest.config.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { defineConfig } from 'vitest/config';
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [svelte()],
|
||||
test: {
|
||||
include: ['src/**/*.{test,spec}.{js,ts}'],
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
},
|
||||
resolve: {
|
||||
conditions: ['browser'],
|
||||
alias: {
|
||||
'$app/environment': new URL('./src/test/mock-environment.ts', import.meta.url).pathname,
|
||||
},
|
||||
},
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue