Files
homelab-topology/src/store/topologyStore.test.ts
Christopher Mayor df02542c26 fix(ui): optimize react state, performance, and ux logic
- Refactored useFilteredNodes to memoize logic and prevent re-rendering.
- Optimized LeftPanel useMemo dependencies and HostChart O(N) traversal.
- Fixed polling interval desync and added WebSocket throttling/React Strict Mode transport patch.
- Fixed Graph layout dependencies and 'ghost' children chevrons on collapsed nodes.
- Fixed RightPanel tab persistence UI bug and resolved React Hooks order crash.

No remaining known issues from the UI analysis.
2026-02-23 15:20:25 -08:00

114 lines
3.3 KiB
TypeScript

import { describe, test, expect, afterEach } from 'vitest';
import { useTopologyStore } from './topologyStore';
import { TopologyNode } from '../types';
const mockNodes: TopologyNode[] = [
{
id: 'gateway-1',
name: 'Gateway',
type: 'gateway',
data: {
status: 'running',
ip: '10.0.0.1',
parentId: undefined,
importance: 5,
metadata: {},
},
},
{
id: 'host-1',
name: 'Ubuntu',
type: 'host_vm',
data: {
status: 'running',
ip: '10.0.0.10',
parentId: 'gateway-1',
importance: 4,
metadata: {},
},
},
{
id: 'service-1',
name: 'Traefik',
type: 'service',
data: {
status: 'running',
ip: '10.0.0.10',
parentId: 'host-1',
category: 'infra',
importance: 5,
metadata: {},
},
},
{
id: 'service-2',
name: 'Plex',
type: 'service',
data: {
status: 'stopped',
ip: '10.0.0.10',
parentId: 'host-1',
category: 'media',
importance: 2,
metadata: {},
},
},
];
describe('topologyStore', () => {
afterEach(() => {
// Reset store state between tests
useTopologyStore.setState({
nodes: [],
edges: [],
selectedNodeId: null,
searchQuery: '',
typeFilters: [],
statusFilter: 'all',
highlightPath: [],
});
});
/* ----------------------------------------------------------------
* Basic state management
* ------------------------------------------------------------- */
describe('setNodes', () => {
test('sets nodes correctly', () => {
useTopologyStore.getState().setNodes(mockNodes);
expect(useTopologyStore.getState().nodes).toHaveLength(4);
});
});
describe('setSelectedNode', () => {
test('selects a node by id', () => {
useTopologyStore.getState().setNodes(mockNodes);
useTopologyStore.getState().setSelectedNode('host-1');
expect(useTopologyStore.getState().selectedNodeId).toBe('host-1');
});
test('clears selection with null', () => {
useTopologyStore.getState().setSelectedNode('host-1');
useTopologyStore.getState().setSelectedNode(null);
expect(useTopologyStore.getState().selectedNodeId).toBeNull();
});
});
/* ----------------------------------------------------------------
* Type filter toggle
* ------------------------------------------------------------- */
describe('toggleTypeFilter', () => {
test('adds type when not present', () => {
useTopologyStore.getState().toggleTypeFilter('gateway');
expect(useTopologyStore.getState().typeFilters).toContain('gateway');
});
test('removes type when already present', () => {
useTopologyStore.getState().toggleTypeFilter('gateway');
useTopologyStore.getState().toggleTypeFilter('gateway');
expect(useTopologyStore.getState().typeFilters).not.toContain('gateway');
});
});
});