- Implemented `owl_to_mermaid.py` to convert OWL/Turtle files into Mermaid class diagrams. - Implemented `owl_to_plantuml.py` to convert OWL/Turtle files into PlantUML class diagrams. - Added two new PlantUML files for custodian multi-aspect diagrams.
331 lines
9.8 KiB
TypeScript
331 lines
9.8 KiB
TypeScript
/**
|
|
* Unit tests for GraphContext
|
|
*/
|
|
|
|
import { renderHook, act } from '@testing-library/react';
|
|
import { describe, it, expect } from 'vitest';
|
|
import { GraphProvider, useGraph } from '../../src/contexts/GraphContext';
|
|
import type { GraphData, GraphNode, GraphLink } from '../../src/types/graph';
|
|
|
|
// ============================================================================
|
|
// Test Utilities
|
|
// ============================================================================
|
|
|
|
function createWrapper() {
|
|
return function Wrapper({ children }: { children: React.ReactNode }) {
|
|
return <GraphProvider>{children}</GraphProvider>;
|
|
};
|
|
}
|
|
|
|
const sampleGraphData: GraphData = {
|
|
nodes: [
|
|
{ id: 'node1', label: 'Node 1', type: 'Resource', uri: 'http://example.org/node1' },
|
|
{ id: 'node2', label: 'Node 2', type: 'Class', uri: 'http://example.org/node2' },
|
|
],
|
|
links: [
|
|
{
|
|
source: 'node1',
|
|
target: 'node2',
|
|
predicate: 'relatesTo',
|
|
isBidirectional: false,
|
|
},
|
|
],
|
|
};
|
|
|
|
const sampleNode: GraphNode = sampleGraphData.nodes[0];
|
|
const sampleLink: GraphLink = sampleGraphData.links[0];
|
|
|
|
// ============================================================================
|
|
// Tests
|
|
// ============================================================================
|
|
|
|
describe('GraphContext', () => {
|
|
describe('Provider and Hook', () => {
|
|
it('should throw error when useGraph is used outside provider', () => {
|
|
expect(() => {
|
|
renderHook(() => useGraph());
|
|
}).toThrow('useGraph must be used within GraphProvider');
|
|
});
|
|
|
|
it('should provide initial state', () => {
|
|
const { result } = renderHook(() => useGraph(), {
|
|
wrapper: createWrapper(),
|
|
});
|
|
|
|
expect(result.current.state.graphData).toBe(null);
|
|
expect(result.current.state.isLoading).toBe(false);
|
|
expect(result.current.state.error).toBe(null);
|
|
expect(result.current.state.selectedNode).toBe(null);
|
|
expect(result.current.state.selectedLink).toBe(null);
|
|
expect(result.current.state.filters).toEqual({
|
|
nodeTypes: [],
|
|
predicates: [],
|
|
searchQuery: '',
|
|
});
|
|
expect(result.current.state.config).toEqual({
|
|
showLabels: true,
|
|
nodeSize: 8,
|
|
linkWidth: 1.5,
|
|
chargeStrength: -300,
|
|
linkDistance: 100,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Graph Data Management', () => {
|
|
it('should set graph data', () => {
|
|
const { result } = renderHook(() => useGraph(), {
|
|
wrapper: createWrapper(),
|
|
});
|
|
|
|
act(() => {
|
|
result.current.actions.setGraphData(sampleGraphData);
|
|
});
|
|
|
|
expect(result.current.state.graphData).toEqual(sampleGraphData);
|
|
expect(result.current.state.isLoading).toBe(false);
|
|
expect(result.current.state.error).toBe(null);
|
|
});
|
|
|
|
it('should set loading state', () => {
|
|
const { result } = renderHook(() => useGraph(), {
|
|
wrapper: createWrapper(),
|
|
});
|
|
|
|
act(() => {
|
|
result.current.actions.setLoading(true);
|
|
});
|
|
|
|
expect(result.current.state.isLoading).toBe(true);
|
|
|
|
act(() => {
|
|
result.current.actions.setLoading(false);
|
|
});
|
|
|
|
expect(result.current.state.isLoading).toBe(false);
|
|
});
|
|
|
|
it('should set error', () => {
|
|
const { result } = renderHook(() => useGraph(), {
|
|
wrapper: createWrapper(),
|
|
});
|
|
|
|
act(() => {
|
|
result.current.actions.setError('Test error');
|
|
});
|
|
|
|
expect(result.current.state.error).toBe('Test error');
|
|
expect(result.current.state.isLoading).toBe(false);
|
|
|
|
act(() => {
|
|
result.current.actions.setError(null);
|
|
});
|
|
|
|
expect(result.current.state.error).toBe(null);
|
|
});
|
|
});
|
|
|
|
describe('Selection Management', () => {
|
|
it('should select node', () => {
|
|
const { result } = renderHook(() => useGraph(), {
|
|
wrapper: createWrapper(),
|
|
});
|
|
|
|
act(() => {
|
|
result.current.actions.selectNode(sampleNode);
|
|
});
|
|
|
|
expect(result.current.state.selectedNode).toEqual(sampleNode);
|
|
expect(result.current.state.selectedLink).toBe(null); // Link should be cleared
|
|
});
|
|
|
|
it('should select link', () => {
|
|
const { result } = renderHook(() => useGraph(), {
|
|
wrapper: createWrapper(),
|
|
});
|
|
|
|
// First select a node
|
|
act(() => {
|
|
result.current.actions.selectNode(sampleNode);
|
|
});
|
|
|
|
expect(result.current.state.selectedNode).toEqual(sampleNode);
|
|
|
|
// Then select a link (should clear node)
|
|
act(() => {
|
|
result.current.actions.selectLink(sampleLink);
|
|
});
|
|
|
|
expect(result.current.state.selectedLink).toEqual(sampleLink);
|
|
expect(result.current.state.selectedNode).toBe(null); // Node should be cleared
|
|
});
|
|
|
|
it('should clear selection', () => {
|
|
const { result } = renderHook(() => useGraph(), {
|
|
wrapper: createWrapper(),
|
|
});
|
|
|
|
// Select node and link
|
|
act(() => {
|
|
result.current.actions.selectNode(sampleNode);
|
|
result.current.actions.selectLink(sampleLink);
|
|
});
|
|
|
|
// Clear selection
|
|
act(() => {
|
|
result.current.actions.clearSelection();
|
|
});
|
|
|
|
expect(result.current.state.selectedNode).toBe(null);
|
|
expect(result.current.state.selectedLink).toBe(null);
|
|
});
|
|
});
|
|
|
|
describe('Filter Management', () => {
|
|
it('should update filters partially', () => {
|
|
const { result } = renderHook(() => useGraph(), {
|
|
wrapper: createWrapper(),
|
|
});
|
|
|
|
act(() => {
|
|
result.current.actions.updateFilters({ nodeTypes: ['Resource', 'Class'] });
|
|
});
|
|
|
|
expect(result.current.state.filters.nodeTypes).toEqual(['Resource', 'Class']);
|
|
expect(result.current.state.filters.predicates).toEqual([]); // Should remain unchanged
|
|
expect(result.current.state.filters.searchQuery).toBe(''); // Should remain unchanged
|
|
});
|
|
|
|
it('should update multiple filters at once', () => {
|
|
const { result } = renderHook(() => useGraph(), {
|
|
wrapper: createWrapper(),
|
|
});
|
|
|
|
act(() => {
|
|
result.current.actions.updateFilters({
|
|
nodeTypes: ['Resource'],
|
|
predicates: ['relatesTo'],
|
|
searchQuery: 'test',
|
|
});
|
|
});
|
|
|
|
expect(result.current.state.filters).toEqual({
|
|
nodeTypes: ['Resource'],
|
|
predicates: ['relatesTo'],
|
|
searchQuery: 'test',
|
|
});
|
|
});
|
|
|
|
it('should reset filters', () => {
|
|
const { result } = renderHook(() => useGraph(), {
|
|
wrapper: createWrapper(),
|
|
});
|
|
|
|
// Set some filters
|
|
act(() => {
|
|
result.current.actions.updateFilters({
|
|
nodeTypes: ['Resource'],
|
|
predicates: ['relatesTo'],
|
|
searchQuery: 'test',
|
|
});
|
|
});
|
|
|
|
expect(result.current.state.filters).not.toEqual({
|
|
nodeTypes: [],
|
|
predicates: [],
|
|
searchQuery: '',
|
|
});
|
|
|
|
// Reset filters
|
|
act(() => {
|
|
result.current.actions.resetFilters();
|
|
});
|
|
|
|
expect(result.current.state.filters).toEqual({
|
|
nodeTypes: [],
|
|
predicates: [],
|
|
searchQuery: '',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Visualization Config Management', () => {
|
|
it('should update config partially', () => {
|
|
const { result } = renderHook(() => useGraph(), {
|
|
wrapper: createWrapper(),
|
|
});
|
|
|
|
act(() => {
|
|
result.current.actions.updateConfig({ nodeSize: 12 });
|
|
});
|
|
|
|
expect(result.current.state.config.nodeSize).toBe(12);
|
|
expect(result.current.state.config.showLabels).toBe(true); // Should remain unchanged
|
|
});
|
|
|
|
it('should update multiple config options at once', () => {
|
|
const { result } = renderHook(() => useGraph(), {
|
|
wrapper: createWrapper(),
|
|
});
|
|
|
|
act(() => {
|
|
result.current.actions.updateConfig({
|
|
showLabels: false,
|
|
nodeSize: 10,
|
|
linkWidth: 2,
|
|
});
|
|
});
|
|
|
|
expect(result.current.state.config.showLabels).toBe(false);
|
|
expect(result.current.state.config.nodeSize).toBe(10);
|
|
expect(result.current.state.config.linkWidth).toBe(2);
|
|
expect(result.current.state.config.chargeStrength).toBe(-300); // Should remain unchanged
|
|
});
|
|
});
|
|
|
|
describe('Clear All', () => {
|
|
it('should reset to initial state', () => {
|
|
const { result } = renderHook(() => useGraph(), {
|
|
wrapper: createWrapper(),
|
|
});
|
|
|
|
// Set various state
|
|
act(() => {
|
|
result.current.actions.setGraphData(sampleGraphData);
|
|
result.current.actions.selectNode(sampleNode);
|
|
result.current.actions.updateFilters({ searchQuery: 'test' });
|
|
result.current.actions.updateConfig({ nodeSize: 10 });
|
|
});
|
|
|
|
// Verify state was set
|
|
expect(result.current.state.graphData).not.toBe(null);
|
|
expect(result.current.state.selectedNode).not.toBe(null);
|
|
expect(result.current.state.filters.searchQuery).toBe('test');
|
|
expect(result.current.state.config.nodeSize).toBe(10);
|
|
|
|
// Clear all
|
|
act(() => {
|
|
result.current.actions.clearAll();
|
|
});
|
|
|
|
// Verify reset to initial state
|
|
expect(result.current.state.graphData).toBe(null);
|
|
expect(result.current.state.isLoading).toBe(false);
|
|
expect(result.current.state.error).toBe(null);
|
|
expect(result.current.state.selectedNode).toBe(null);
|
|
expect(result.current.state.selectedLink).toBe(null);
|
|
expect(result.current.state.filters).toEqual({
|
|
nodeTypes: [],
|
|
predicates: [],
|
|
searchQuery: '',
|
|
});
|
|
expect(result.current.state.config).toEqual({
|
|
showLabels: true,
|
|
nodeSize: 8,
|
|
linkWidth: 1.5,
|
|
chargeStrength: -300,
|
|
linkDistance: 100,
|
|
});
|
|
});
|
|
});
|
|
});
|