151 lines
5.4 KiB
TypeScript
151 lines
5.4 KiB
TypeScript
import { describe, beforeEach } from '@jest/globals';
|
|
import { screen } from '@testing-library/react';
|
|
import { renderWithProviders } from '../../../../test-utils/test-utils.tsx';
|
|
import type { XYPosition } from '@xyflow/react';
|
|
import { NodeTypes, NodeDefaults, NodeConnections, NodeReduces, NodesInPhase } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/NodeRegistry';
|
|
import '@testing-library/jest-dom'
|
|
import { createElement } from 'react';
|
|
import useFlowStore from '../../../../../src/pages/VisProgPage/visualProgrammingUI/VisProgStores';
|
|
|
|
|
|
describe('NormNode', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
function createNode(id: string, type: string, position: XYPosition, data: Record<string, unknown>, deletable? : boolean) {
|
|
const defaultData = NodeDefaults[type as keyof typeof NodeDefaults]
|
|
|
|
return {
|
|
id: id,
|
|
type: type,
|
|
position: position,
|
|
data: {...defaultData, ...data},
|
|
deletable: deletable
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Reduces the graph into its phases' information and recursively calls their reducing function
|
|
*/
|
|
function graphReducer() {
|
|
const { nodes } = useFlowStore.getState();
|
|
return nodes
|
|
.filter((n) => n.type == 'phase')
|
|
.map((n) => {
|
|
const reducer = NodeReduces['phase'];
|
|
return reducer(n, nodes)
|
|
});
|
|
}
|
|
|
|
function getAllTypes() {
|
|
return Object.entries(NodeTypes).map(([t])=>t)
|
|
}
|
|
|
|
describe('Rendering', () => {
|
|
test.each(getAllTypes())('it should render %s node with the default data', (nodeType) => {
|
|
const lengthBefore = screen.getAllByText(/.*/).length;
|
|
|
|
const newNode = createNode(nodeType + "1", nodeType, {x: 200, y:200}, {});
|
|
|
|
const found = Object.entries(NodeTypes).find(([t]) => t === nodeType);
|
|
const uiElement = found ? found[1] : null;
|
|
|
|
expect(uiElement).not.toBeNull();
|
|
const props = {
|
|
id: newNode.id,
|
|
type: newNode.type as string,
|
|
data: newNode.data as any,
|
|
selected: false,
|
|
isConnectable: true,
|
|
zIndex: 0,
|
|
dragging: false,
|
|
selectable: true,
|
|
deletable: true,
|
|
draggable: true,
|
|
positionAbsoluteX: 0,
|
|
positionAbsoluteY: 0,
|
|
};
|
|
|
|
renderWithProviders(createElement(uiElement as React.ComponentType<any>, props));
|
|
const lengthAfter = screen.getAllByText(/.*/).length;
|
|
|
|
expect(lengthBefore + 1 === lengthAfter);
|
|
});
|
|
|
|
});
|
|
|
|
|
|
describe('Connecting', () => {
|
|
test.each(getAllTypes())('it should call the connect function when %s node is connected', (nodeType) => {
|
|
// Create two nodes - one of the current type and one to connect to
|
|
const sourceNode = createNode('source-1', nodeType, {x: 100, y: 100}, {});
|
|
const targetNode = createNode('target-1', 'end', {x: 300, y: 100}, {});
|
|
|
|
// Add nodes to store
|
|
useFlowStore.setState({ nodes: [sourceNode, targetNode] });
|
|
|
|
// Spy on the connect functions
|
|
const sourceConnectSpy = jest.spyOn(NodeConnections.Sources, nodeType as keyof typeof NodeConnections.Sources);
|
|
const targetConnectSpy = jest.spyOn(NodeConnections.Targets, 'end');
|
|
|
|
// Simulate connection
|
|
useFlowStore.getState().onConnect({
|
|
source: 'source-1',
|
|
target: 'target-1',
|
|
sourceHandle: null,
|
|
targetHandle: null,
|
|
});
|
|
|
|
// Verify the connect functions were called
|
|
expect(sourceConnectSpy).toHaveBeenCalledWith(sourceNode, targetNode.id);
|
|
expect(targetConnectSpy).toHaveBeenCalledWith(targetNode, sourceNode.id);
|
|
|
|
sourceConnectSpy.mockRestore();
|
|
targetConnectSpy.mockRestore();
|
|
});
|
|
});
|
|
|
|
describe('Reducing', () => {
|
|
test.each(getAllTypes())('it should correctly call/ not call the reduce function when %s node is in a phase', (nodeType) => {
|
|
// Create a phase node and a node of the current type
|
|
const phaseNode = createNode('phase-1', 'phase', {x: 200, y: 100}, { label: 'Test Phase', children: [] });
|
|
const testNode = createNode('node-1', nodeType, {x: 100, y: 100}, {});
|
|
|
|
// Add the test node as a child of the phase
|
|
(phaseNode.data as any).children.push(testNode.id);
|
|
|
|
// Add nodes to store
|
|
useFlowStore.setState({ nodes: [phaseNode, testNode] });
|
|
|
|
// Spy on the reduce functions
|
|
const phaseReduceSpy = jest.spyOn(NodeReduces, 'phase');
|
|
const nodeReduceSpy = jest.spyOn(NodeReduces, nodeType as keyof typeof NodeReduces);
|
|
|
|
// Simulate reducing - using the graphReducer
|
|
const result = graphReducer();
|
|
|
|
// Verify the reduce functions were called
|
|
expect(phaseReduceSpy).toHaveBeenCalledWith(phaseNode, [phaseNode, testNode]);
|
|
// Check if this node type is in NodesInPhase and returns false
|
|
const nodesInPhaseFunc = NodesInPhase[nodeType as keyof typeof NodesInPhase];
|
|
if (nodesInPhaseFunc && !nodesInPhaseFunc() && nodeType !== 'phase') {
|
|
// Node is NOT in phase, so it should NOT be called
|
|
expect(nodeReduceSpy).not.toHaveBeenCalled();
|
|
} else {
|
|
// Node IS in phase, so it SHOULD be called
|
|
expect(nodeReduceSpy).toHaveBeenCalled();
|
|
}
|
|
|
|
// Verify the correct structure is present using NodesInPhase
|
|
expect(result).toHaveLength(nodeType !== 'phase' ? 1 : 2);
|
|
expect(result[0]).toHaveProperty('id', 'phase-1');
|
|
expect(result[0]).toHaveProperty('label', 'Test Phase');
|
|
|
|
// Restore mocks
|
|
phaseReduceSpy.mockRestore();
|
|
nodeReduceSpy.mockRestore();
|
|
});
|
|
});
|
|
}); |