diff --git a/tests/stores/mapPathStore.test.js b/tests/stores/mapPathStore.test.js new file mode 100644 index 0000000..b56be6b --- /dev/null +++ b/tests/stores/mapPathStore.test.js @@ -0,0 +1,169 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { setActivePinia, createPinia } from 'pinia'; + +// Mock allMapData store (used by createInsightWithPath / createPaths) +vi.mock('@/stores/allMapData.js', () => ({ + default: () => ({ insights: {} }), +})); + +// Mock SVG asset imports +vi.mock('@/assets/capsule1-glow.svg', () => ({ default: 'glow1' })); +vi.mock('@/assets/capsule2-glow.svg', () => ({ default: 'glow2' })); +vi.mock('@/assets/capsule3-glow.svg', () => ({ default: 'glow3' })); +vi.mock('@/assets/capsule4-glow.svg', () => ({ default: 'glow4' })); +vi.mock('@/assets/capsule1.svg', () => ({ default: 'cap1' })); +vi.mock('@/assets/capsule2.svg', () => ({ default: 'cap2' })); +vi.mock('@/assets/capsule3.svg', () => ({ default: 'cap3' })); +vi.mock('@/assets/capsule4.svg', () => ({ default: 'cap4' })); + +import useMapPathStore from '@/stores/mapPathStore.ts'; + +/** + * Creates a mock Cytoscape node. + */ +function mockNode(label, level = 0) { + const nodeData = { label, level, nodeImageUrl: '' }; + const classes = new Set(); + const node = { + data: vi.fn((key, value) => { + if (value !== undefined) { + nodeData[key] = value; + return node; + } + return nodeData[key]; + }), + addClass: vi.fn((cls) => { classes.add(cls); return node; }), + removeClass: vi.fn((cls) => { classes.delete(cls); return node; }), + hasClass: (cls) => classes.has(cls), + outgoers: vi.fn(() => mockCollection([])), + incomers: vi.fn(() => mockCollection([])), + edgesTo: vi.fn(() => mockCollection([])), + edgesWith: vi.fn(() => mockCollection([])), + _nodeData: nodeData, + _classes: classes, + }; + return node; +} + +/** + * Creates a mock Cytoscape edge. + */ +function mockEdge() { + const classes = new Set(); + const edge = { + addClass: vi.fn((cls) => { classes.add(cls); return edge; }), + removeClass: vi.fn((cls) => { classes.delete(cls); return edge; }), + source: vi.fn(() => mockNode('src')), + target: vi.fn(() => mockNode('tgt')), + _classes: classes, + }; + return edge; +} + +/** + * Creates a mock Cytoscape collection. + */ +function mockCollection(items) { + const col = { + forEach: (fn) => items.forEach(fn), + map: (fn) => items.map(fn), + filter: (fn) => mockCollection(items.filter(fn)), + addClass: vi.fn(() => col), + removeClass: vi.fn(() => col), + length: items.length, + }; + return col; +} + +/** + * Creates a mock Cytoscape instance. + */ +function mockCytoscape(nodes = [], edges = []) { + return { + nodes: vi.fn(() => mockCollection(nodes)), + edges: vi.fn(() => mockCollection(edges)), + }; +} + +describe('mapPathStore', () => { + let store; + + beforeEach(() => { + setActivePinia(createPinia()); + store = useMapPathStore(); + vi.clearAllMocks(); + }); + + it('has correct default state', () => { + expect(store.processOrBPMN).toBe('process'); + expect(store.curveType).toBe('curved'); + expect(store.directionType).toBe('horizontal'); + expect(store.isBPMNOn).toBe(false); + expect(store.allPaths).toEqual([]); + expect(store.activeTrace).toBe(0); + expect(store.lastClickedNode).toBeNull(); + }); + + it('setIsBPMNOn updates state', () => { + store.setIsBPMNOn(true); + expect(store.isBPMNOn).toBe(true); + store.setIsBPMNOn(false); + expect(store.isBPMNOn).toBe(false); + }); + + describe('clearAllHighlight', () => { + it('removes highlight classes from all nodes and edges', () => { + const node1 = mockNode('A', 0); + const edge1 = mockEdge(); + const cy = mockCytoscape([node1], [edge1]); + store.cytoscape.process.curved.horizontal = cy; + + store.clearAllHighlight(); + + expect(cy.edges).toHaveBeenCalled(); + expect(cy.nodes).toHaveBeenCalled(); + }); + + it('does not throw when cytoscape is null', () => { + store.cytoscape.process.curved.horizontal = null; + expect(() => store.clearAllHighlight()).not.toThrow(); + }); + }); + + describe('onNodeClickHighlightEdges', () => { + it('highlights clicked node and its edges', () => { + const node = mockNode('Activity', 1); + const outEdge = mockEdge(); + const inEdge = mockEdge(); + node.outgoers.mockReturnValue(mockCollection([outEdge])); + node.incomers.mockReturnValue(mockCollection([inEdge])); + + store.cytoscape.process.curved.horizontal = mockCytoscape(); + + store.onNodeClickHighlightEdges(node); + + expect(node.addClass).toHaveBeenCalledWith('highlight-node'); + expect(outEdge.addClass).toHaveBeenCalledWith('highlight-edge'); + expect(inEdge.addClass).toHaveBeenCalledWith('highlight-edge'); + expect(store.lastClickedNode).toStrictEqual(node); + }); + }); + + describe('onEdgeClickHighlightNodes', () => { + it('highlights source and target nodes of clicked edge', () => { + const src = mockNode('Start', 0); + const tgt = mockNode('End', 2); + const edge = mockEdge(); + edge.source.mockReturnValue(src); + edge.target.mockReturnValue(tgt); + + store.cytoscape.process.curved.horizontal = mockCytoscape(); + + store.onEdgeClickHighlightNodes(edge); + + expect(src.addClass).toHaveBeenCalledWith('highlight-node'); + expect(tgt.addClass).toHaveBeenCalledWith('highlight-node'); + expect(edge.addClass).toHaveBeenCalledWith('highlight-edge'); + }); + }); +});