Running the CI Tests results in errors (see the run here: https://github.com/OFFIS-ESC/constellation-analyzer/actions/runs/19229156468 ). Why is that? (vibe-kanban b6717985)

This commit is contained in:
Jan-Henrik Bruhn 2025-11-10 12:13:07 +01:00
parent 97583e412a
commit 28719d8953
5 changed files with 63 additions and 43 deletions

View file

@ -1,6 +1,6 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { useGraphStore } from './graphStore';
import type { Actor, Relation, Group, NodeTypeConfig, EdgeTypeConfig, LabelConfig } from '../types';
import type { Actor, Relation, Group, NodeTypeConfig, EdgeTypeConfig, LabelConfig, NodeShape } from '../types';
import { MINIMIZED_GROUP_WIDTH, MINIMIZED_GROUP_HEIGHT } from '../constants';
// Helper to create a mock node
@ -10,8 +10,8 @@ function createMockNode(id: string, actorType: string = 'person'): Actor {
type: 'custom',
position: { x: 100, y: 100 },
data: {
actorType,
name: `Test ${id}`,
type: actorType,
label: `Test ${id}`,
description: 'Test description',
},
};
@ -25,7 +25,7 @@ function createMockEdge(id: string, source: string, target: string, relationType
target,
type: 'custom',
data: {
relationType,
type: relationType,
description: 'Test relation',
},
};
@ -39,6 +39,7 @@ function createMockGroup(id: string, actorIds: string[] = []): Group {
position: { x: 0, y: 0 },
data: {
label: `Group ${id}`,
color: '#cccccc',
actorIds,
minimized: false,
},
@ -158,33 +159,35 @@ describe('graphStore', () => {
const { updateNode } = useGraphStore.getState();
updateNode('node-1', {
data: { name: 'Updated Name', actorType: 'person' },
data: { label: 'Updated Name', type: 'person' },
});
const state = useGraphStore.getState();
expect(state.nodes[0].data.name).toBe('Updated Name');
expect(state.nodes[0].data.label).toBe('Updated Name');
});
it('should merge data instead of replacing', () => {
const { updateNode } = useGraphStore.getState();
updateNode('node-1', {
data: { description: 'New description' },
data: { label: 'Test node-1', type: 'person', description: 'New description' },
});
const state = useGraphStore.getState();
expect(state.nodes[0].data.name).toBe('Test node-1'); // Preserved
expect(state.nodes[0].data.label).toBe('Test node-1'); // Preserved
expect(state.nodes[0].data.description).toBe('New description'); // Updated
});
it('should validate labels against existing labels', () => {
const { addLabel, updateNode } = useGraphStore.getState();
addLabel({ id: 'label-1', label: 'Valid', color: '#000' });
addLabel({ id: 'label-2', label: 'Also Valid', color: '#111' });
addLabel({ id: 'label-1', name: 'Valid', color: '#000', appliesTo: 'actors' });
addLabel({ id: 'label-2', name: 'Also Valid', color: '#111', appliesTo: 'actors' });
updateNode('node-1', {
data: {
label: 'Test node-1',
type: 'person',
labels: ['label-1', 'label-999', 'label-2'], // label-999 doesn't exist
},
});
@ -198,6 +201,8 @@ describe('graphStore', () => {
updateNode('node-1', {
data: {
label: 'Test node-1',
type: 'person',
labels: ['invalid-1', 'invalid-2'],
},
});
@ -359,7 +364,7 @@ describe('graphStore', () => {
it('should validate labels against existing labels', () => {
const { addLabel, updateEdge } = useGraphStore.getState();
addLabel({ id: 'label-1', label: 'Valid', color: '#000' });
addLabel({ id: 'label-1', name: 'Valid', color: '#000', appliesTo: 'relations' });
updateEdge('edge-1', {
labels: ['label-1', 'invalid-label'],
@ -805,8 +810,9 @@ describe('graphStore', () => {
const label: LabelConfig = {
id: 'label-1',
label: 'Important',
name: 'Important',
color: '#ff0000',
appliesTo: 'both',
};
addLabel(label);
@ -820,16 +826,16 @@ describe('graphStore', () => {
describe('updateLabel', () => {
beforeEach(() => {
const { addLabel } = useGraphStore.getState();
addLabel({ id: 'label-1', label: 'Test', color: '#000' });
addLabel({ id: 'label-1', name: 'Test', color: '#000', appliesTo: 'both' });
});
it('should update label', () => {
const { updateLabel } = useGraphStore.getState();
updateLabel('label-1', { label: 'Updated', color: '#fff' });
updateLabel('label-1', { name: 'Updated', color: '#fff' });
const state = useGraphStore.getState();
expect(state.labels[0].label).toBe('Updated');
expect(state.labels[0].name).toBe('Updated');
expect(state.labels[0].color).toBe('#fff');
});
});
@ -837,8 +843,8 @@ describe('graphStore', () => {
describe('deleteLabel', () => {
beforeEach(() => {
const { addNode, addEdge, addLabel } = useGraphStore.getState();
addLabel({ id: 'label-1', label: 'Test', color: '#000' });
addLabel({ id: 'label-2', label: 'Other', color: '#111' });
addLabel({ id: 'label-1', name: 'Test', color: '#000', appliesTo: 'both' });
addLabel({ id: 'label-2', name: 'Other', color: '#111', appliesTo: 'both' });
// Add nodes and edges with labels
const node = createMockNode('node-1');
@ -846,7 +852,7 @@ describe('graphStore', () => {
addNode(node);
const edge = createMockEdge('edge-1', 'node-1', 'node-1');
edge.data = { ...edge.data, labels: ['label-1'] };
edge.data = { ...edge.data, type: 'collaborates', labels: ['label-1'] };
addEdge(edge);
});
@ -972,7 +978,7 @@ describe('graphStore', () => {
it('should set labels', () => {
const { setLabels } = useGraphStore.getState();
const labels: LabelConfig[] = [
{ id: 'label-1', label: 'Test', color: '#000' },
{ id: 'label-1', name: 'Test', color: '#000', appliesTo: 'both' },
];
setLabels(labels);
@ -991,13 +997,13 @@ describe('graphStore', () => {
edges: [createMockEdge('edge-1', 'node-1', 'node-1')],
groups: [createMockGroup('group-1')],
nodeTypes: [
{ id: 'custom', label: 'Custom', color: '#000', shape: 'circle', icon: 'Test', description: 'Test' },
{ id: 'custom', label: 'Custom', color: '#000', shape: 'circle' as NodeShape, icon: 'Test', description: 'Test' },
],
edgeTypes: [
{ id: 'custom', label: 'Custom', color: '#000', style: 'solid' },
{ id: 'custom', label: 'Custom', color: '#000', style: 'solid' as const },
],
labels: [
{ id: 'label-1', label: 'Test', color: '#000' },
{ id: 'label-1', name: 'Test', color: '#000', appliesTo: 'both' as const },
],
};

View file

@ -327,7 +327,7 @@ describe('historyStore', () => {
id: 'node-1',
type: 'custom',
position: { x: 100, y: 100 },
data: { actorType: 'person', name: 'Test' },
data: { type: 'person', label: 'Test' },
},
],
edges: [],
@ -342,9 +342,9 @@ describe('historyStore', () => {
// Snapshot is serialized (Map -> object) during pushAction
// Need to access states as a record object, not a Map
const states = snapshot?.timeline.states as Record<string, unknown>;
const states = snapshot?.timeline.states as Record<string, unknown> | undefined;
const currentStateId = snapshot?.timeline.currentStateId;
const currentState = states[currentStateId] as { graph: { nodes: unknown[] } };
const currentState = currentStateId && states ? states[currentStateId] as { graph: { nodes: unknown[] } } : undefined;
expect(currentState?.graph.nodes).toHaveLength(1);
});
@ -872,7 +872,7 @@ describe('historyStore', () => {
const history = state.histories.get(TEST_DOC_ID);
// Should have consistent state
expect(history?.undoStack.length + history?.redoStack.length).toBe(10);
expect((history?.undoStack.length ?? 0) + (history?.redoStack.length ?? 0)).toBe(10);
});
it('should handle empty snapshots', () => {

View file

@ -1,5 +1,7 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { useTimelineStore } from './timelineStore';
import type { Timeline, ConstellationState } from '../types/timeline';
import type { Actor, Relation, Group, NodeTypeConfig, EdgeTypeConfig, LabelConfig } from '../types';
// Mock dependent stores
const mockShowToast = vi.fn();
@ -8,7 +10,15 @@ const mockLoadGraphState = vi.fn();
const mockPushToHistory = vi.fn();
// Create a mutable mock state for graphStore
const mockGraphState = {
const mockGraphState: {
nodes: Actor[];
edges: Relation[];
groups: Group[];
nodeTypes: NodeTypeConfig[];
edgeTypes: EdgeTypeConfig[];
labels: LabelConfig[];
loadGraphState: typeof mockLoadGraphState;
} = {
nodes: [],
edges: [],
groups: [],
@ -133,7 +143,7 @@ describe('timelineStore', () => {
const { initializeTimeline } = useTimelineStore.getState();
const initialGraph = {
nodes: [{ id: 'node-1', type: 'custom', position: { x: 0, y: 0 }, data: {} }],
nodes: [{ id: 'node-1', type: 'custom', position: { x: 0, y: 0 }, data: { label: 'Test', type: 'person' } }],
edges: [],
groups: [],
};
@ -141,7 +151,7 @@ describe('timelineStore', () => {
initializeTimeline(TEST_DOC_ID, initialGraph);
// Modify original
initialGraph.nodes.push({ id: 'node-2', type: 'custom', position: { x: 0, y: 0 }, data: {} });
initialGraph.nodes.push({ id: 'node-2', type: 'custom', position: { x: 0, y: 0 }, data: { label: 'Test', type: 'person' } });
// Timeline should be unaffected
const state = useTimelineStore.getState();
@ -184,7 +194,11 @@ describe('timelineStore', () => {
const { loadTimeline } = useTimelineStore.getState();
// Simulate loaded JSON (states as plain object)
const timelineFromJSON = {
const timelineFromJSON: {
states: Record<string, ConstellationState>;
currentStateId: string;
rootStateId: string;
} = {
states: {
'state-1': {
id: 'state-1',
@ -199,7 +213,7 @@ describe('timelineStore', () => {
rootStateId: 'state-1',
};
loadTimeline(TEST_DOC_ID, timelineFromJSON as Timeline);
loadTimeline(TEST_DOC_ID, timelineFromJSON as unknown as Timeline);
const state = useTimelineStore.getState();
const timeline = state.timelines.get(TEST_DOC_ID);
@ -233,7 +247,7 @@ describe('timelineStore', () => {
const { createState } = useTimelineStore.getState();
// Simulate current graph with nodes by mutating mockGraphState
mockGraphState.nodes = [{ id: 'node-1', type: 'custom', position: { x: 0, y: 0 }, data: {} }];
mockGraphState.nodes = [{ id: 'node-1', type: 'custom', position: { x: 0, y: 0 }, data: { label: 'Test', type: 'person' } }];
const newStateId = createState('With Nodes');
@ -345,7 +359,7 @@ describe('timelineStore', () => {
const { switchToState, getAllStates } = useTimelineStore.getState();
// Mock current graph with nodes by mutating mockGraphState
mockGraphState.nodes = [{ id: 'node-modified', type: 'custom', position: { x: 100, y: 100 }, data: {} }];
mockGraphState.nodes = [{ id: 'node-modified', type: 'custom', position: { x: 100, y: 100 }, data: { label: 'Test', type: 'person' } }];
const states = getAllStates();
const currentStateId = states[2].id; // Current is State 3
@ -415,13 +429,13 @@ describe('timelineStore', () => {
it('should merge metadata', () => {
const { updateState, getState } = useTimelineStore.getState();
updateState(stateId, { metadata: { custom: 'value1' } });
updateState(stateId, { metadata: { another: 'value2' } });
updateState(stateId, { metadata: { date: '2024-01-01' } });
updateState(stateId, { metadata: { color: '#FF0000' } });
const updatedState = getState(stateId);
expect(updatedState?.metadata).toEqual({
custom: 'value1',
another: 'value2',
date: '2024-01-01',
color: '#FF0000',
});
});
@ -574,7 +588,7 @@ describe('timelineStore', () => {
const originalState = timeline?.states.get(stateId);
if (originalState) {
originalState.graph = {
nodes: [{ id: 'node-1', type: 'custom', position: { x: 0, y: 0 }, data: {} }],
nodes: [{ id: 'node-1', type: 'custom', position: { x: 0, y: 0 }, data: { label: 'Test', type: 'person' } }],
edges: [],
groups: [],
};
@ -587,7 +601,7 @@ describe('timelineStore', () => {
// Modify original
if (originalState) {
originalState.graph.nodes.push({ id: 'node-2', type: 'custom', position: { x: 0, y: 0 }, data: {} });
originalState.graph.nodes.push({ id: 'node-2', type: 'custom', position: { x: 0, y: 0 }, data: { label: 'Test', type: 'person' } });
}
// Duplicate should be unaffected
@ -702,7 +716,7 @@ describe('timelineStore', () => {
const currentStateId = timeline?.currentStateId;
const newGraph = {
nodes: [{ id: 'node-1', type: 'custom', position: { x: 0, y: 0 }, data: {} }],
nodes: [{ id: 'node-1', type: 'custom', position: { x: 0, y: 0 }, data: { label: 'Test', type: 'person' } }],
edges: [],
groups: [],
};

View file

@ -39,7 +39,7 @@ export function useActiveDocument() {
const graphLabels = useGraphStore((state) => state.labels);
// Track unload timers for inactive documents
const unloadTimersRef = useRef<Map<string, number>>(new Map());
const unloadTimersRef = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());
// Track when we're loading a document to prevent false dirty marking
const isLoadingRef = useRef(false);

View file

@ -22,8 +22,8 @@ export const mockEdgeTypes: EdgeTypeConfig[] = [
// Mock default labels
export const mockLabels: LabelConfig[] = [
{ id: 'label-1', label: 'Important', color: '#ef4444' },
{ id: 'label-2', label: 'Archive', color: '#6b7280' },
{ id: 'label-1', name: 'Important', color: '#ef4444', appliesTo: 'both' },
{ id: 'label-2', name: 'Archive', color: '#6b7280', appliesTo: 'both' },
];
// Create a mock document