import { beforeEach, describe, expect, test } from 'vitest'; import { createDataset } from '@core/dataset'; import { createSnippet } from '@core/snippet'; import { useAppStore } from '../stores/AppStore'; import { useChartBuilderStore } from '../stores/ChartBuilderStore'; import { useDatasetStore } from '../stores/DatasetStore'; import { useSnippetStore } from '../stores/SnippetStore'; import { applyView, deriveViewState } from './UrlStateSync'; const T = new Date('2026-06-01T00:00:00Z'); const seedDataset = (id: number, name: string) => createDataset({ id, name, data: [{ a: 1 }], format: 'json', source: 'inline', now: T }); beforeEach(() => { useSnippetStore.getState().reset(); useDatasetStore.getState().reset(); useChartBuilderStore.setState({ datasetId: null }); useAppStore.getState().setActiveModal(null); window.location.hash = ''; }); describe('deriveViewState — the URL is derived from store state', () => { test('no snippet, no modal → default snippets view', () => { useSnippetStore.getState().hydrate([], null); expect(deriveViewState()).toEqual({ kind: 'snippets' }); }); test('an active snippet → #snippet-', () => { useSnippetStore.getState().hydrate([createSnippet({ id: 's1', now: T })], 's1'); expect(deriveViewState()).toEqual({ kind: 'snippet', snippetId: 's1' }); }); test('Datasets manager (list) → #datasets', () => { useAppStore.getState().setActiveModal('datasets'); expect(deriveViewState()).toEqual({ kind: 'datasets' }); }); test('a selected dataset → #datasets/dataset-', () => { useDatasetStore.getState().hydrate([seedDataset(7, 'Sales')]); useDatasetStore.getState().select(7); useAppStore.getState().setActiveModal('datasets'); expect(deriveViewState()).toEqual({ kind: 'dataset', datasetId: 7 }); }); test('the new-dataset form → #datasets/new', () => { useDatasetStore.getState().startCreate(); useAppStore.getState().setActiveModal('datasets'); expect(deriveViewState()).toEqual({ kind: 'dataset-new' }); }); test('the Chart Builder → #datasets/dataset-/build', () => { useChartBuilderStore.setState({ datasetId: 42 }); useAppStore.getState().setActiveModal('chartBuilder'); expect(deriveViewState()).toEqual({ kind: 'dataset-build', datasetId: 42 }); }); test('a non-navigable modal (extract) leaves the underlying snippet view in the URL', () => { useSnippetStore.getState().hydrate([createSnippet({ id: 's1', now: T })], 's1'); useAppStore.getState().setActiveModal('extract'); expect(deriveViewState()).toEqual({ kind: 'snippet', snippetId: 's1' }); }); }); describe('applyView — the hash drives the stores (restore / Back-Forward)', () => { test('restoring a snippet selects it and closes any modal', () => { useSnippetStore.getState().hydrate([createSnippet({ id: 's1', now: T })], null); useAppStore.getState().setActiveModal('datasets'); applyView({ kind: 'snippet', snippetId: 's1' }); expect(useAppStore.getState().activeModal).toBeNull(); expect(useSnippetStore.getState().activeSnippetId).toBe('s1'); }); test('a dead snippet id falls back to the default view and cleans the URL', () => { useSnippetStore.getState().hydrate([], null); window.location.hash = '#snippet-gone'; applyView({ kind: 'snippet', snippetId: 'gone' }); expect(useSnippetStore.getState().activeSnippetId).toBeNull(); expect(window.location.hash).toBe(''); }); test('restoring the Datasets list opens the manager on its list view', () => { applyView({ kind: 'datasets' }); expect(useAppStore.getState().activeModal).toBe('datasets'); expect(useDatasetStore.getState().view).toBe('list'); }); test('restoring a specific dataset selects it in the open manager', () => { useDatasetStore.getState().hydrate([seedDataset(7, 'Sales')]); applyView({ kind: 'dataset', datasetId: 7 }); expect(useAppStore.getState().activeModal).toBe('datasets'); expect(useDatasetStore.getState().selectedId).toBe(7); expect(useDatasetStore.getState().view).toBe('detail'); }); test('a dead dataset id degrades to the Datasets list and cleans the URL', () => { useDatasetStore.getState().hydrate([]); window.location.hash = '#datasets/dataset-99'; applyView({ kind: 'dataset', datasetId: 99 }); expect(useAppStore.getState().activeModal).toBe('datasets'); expect(useDatasetStore.getState().selectedId).toBeNull(); expect(window.location.hash).toBe('#datasets'); }); test('restoring the new-dataset form opens the manager on its create view', () => { applyView({ kind: 'dataset-new' }); expect(useAppStore.getState().activeModal).toBe('datasets'); expect(useDatasetStore.getState().view).toBe('new'); }); test('restoring the Chart Builder loads its dataset and opens the builder', () => { useDatasetStore.getState().hydrate([seedDataset(7, 'Sales')]); applyView({ kind: 'dataset-build', datasetId: 7 }); expect(useAppStore.getState().activeModal).toBe('chartBuilder'); expect(useChartBuilderStore.getState().datasetId).toBe(7); }); }); describe('round-trip: applyView then deriveViewState is identity', () => { test('every store-representable view survives the round trip', () => { useSnippetStore.getState().hydrate([createSnippet({ id: 's1', now: T })], null); useDatasetStore.getState().hydrate([seedDataset(7, 'Sales')]); const views = [ { kind: 'snippets' as const }, { kind: 'snippet' as const, snippetId: 's1' }, { kind: 'datasets' as const }, { kind: 'dataset' as const, datasetId: 7 }, { kind: 'dataset-new' as const }, { kind: 'dataset-build' as const, datasetId: 7 }, ]; for (const view of views) { applyView(view); expect(deriveViewState()).toEqual(view); } }); });