test: create universal tests and rewrite nodes to have optional parameters for more code coverage
ref: N25B-362
This commit is contained in:
@@ -2,15 +2,19 @@ import { describe, beforeEach } from '@jest/globals';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { renderWithProviders, resetFlowStore } from '../.././/./../../test-utils/test-utils';
|
||||
import type { XYPosition } from '@xyflow/react';
|
||||
import { NodeTypes, NodeDefaults } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/NodeRegistry';
|
||||
import { NodeTypes, NodeDefaults, NodeConnects, 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', () => {
|
||||
// let user: ReturnType<typeof userEvent.setup>;
|
||||
beforeEach(() => {
|
||||
resetFlowStore();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
// Copied from VisStores.
|
||||
function createNode(id: string, type: string, position: XYPosition, data: Record<string, unknown>, deletable? : boolean) {
|
||||
function createNode(id: string, type: string, position: XYPosition, data: Record<string, unknown>, deletable?: boolean) {
|
||||
const defaultData = NodeDefaults[type as keyof typeof NodeDefaults]
|
||||
const newData = {
|
||||
id: id,
|
||||
@@ -21,35 +25,122 @@ describe('NormNode', () => {
|
||||
}
|
||||
return {...defaultData, ...newData}
|
||||
}
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
resetFlowStore();
|
||||
// user = userEvent.setup();
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
let newNode = createNode(nodeType + "1", nodeType, {x: 200, y:200}, {})
|
||||
let uiElement = Object.entries(NodeTypes).find(([t])=>t==nodeType)?.[1]!;
|
||||
let 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('Rendering', () => {
|
||||
test.each([Object.entries(NodeTypes)].map(([t])=>t))('it should render each node with the default data', (nodeType) => {
|
||||
let newNode = createNode(nodeType + "1", nodeType, {x: 200, y:200}, {})
|
||||
let uiElement = Object.entries(NodeTypes).find(([t])=>t==nodeType)?.[1]!;
|
||||
let 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(uiElement(props));
|
||||
const elements = screen.queryAllByText((content, ) =>
|
||||
content.toLowerCase().includes(nodeType.toLowerCase())
|
||||
);
|
||||
expect(elements.length).toBeGreaterThan(0);
|
||||
|
||||
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(NodeConnects, nodeType as keyof typeof NodeConnects);
|
||||
const targetConnectSpy = jest.spyOn(NodeConnects, '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, true);
|
||||
expect(targetConnectSpy).toHaveBeenCalledWith(targetNode, sourceNode, false);
|
||||
|
||||
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() === false && 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();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user