// This program has been developed by students from the bachelor Computer Science at Utrecht // University within the Software Project course. // © Copyright Utrecht University (Department of Information and Computing Sciences) import { describe, it, beforeEach } from '@jest/globals'; import { screen } from '@testing-library/react'; import { renderWithProviders } from '../../../../test-utils/test-utils.tsx'; import TriggerNode, { TriggerReduce, type TriggerNodeData, TriggerConnectionSource, TriggerConnectionTarget } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/TriggerNode'; import useFlowStore from '../../../../../src/pages/VisProgPage/visualProgrammingUI/VisProgStores'; import type { Node } from '@xyflow/react'; import '@testing-library/jest-dom'; import { TriggerNodeDefaults } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/TriggerNode.default.ts'; import { BasicBeliefNodeDefaults } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/BasicBeliefNode.default.ts'; import { defaultPlan } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/Plan.default.ts'; import { NormNodeDefaults } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/NormNode.default.ts'; import { GoalNodeDefaults } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/GoalNode.default.ts'; import { act } from '@testing-library/react'; describe('TriggerNode', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('Rendering', () => { it('should render TriggerNode with keywords type', () => { const mockNode: Node = { id: 'trigger-1', type: 'trigger', position: { x: 0, y: 0 }, data: { ...JSON.parse(JSON.stringify(TriggerNodeDefaults)), }, }; renderWithProviders( ); expect(screen.getByText(/Triggers when the condition is met/i)).toBeInTheDocument(); expect(screen.getByText(/Belief is currently/i)).toBeInTheDocument(); expect(screen.getByText(/Plan is currently/i)).toBeInTheDocument(); }); }); describe('TriggerReduce Function', () => { it('should reduce a trigger node to its essential data', () => { const conditionNode: Node = { id: 'belief-1', type: 'basic_belief', position: { x: 0, y: 0 }, data: { ...JSON.parse(JSON.stringify(BasicBeliefNodeDefaults)), }, }; const triggerNode: Node = { id: 'trigger-1', type: 'trigger', position: { x: 0, y: 0 }, data: { ...JSON.parse(JSON.stringify(TriggerNodeDefaults)), condition: "belief-1", plan: defaultPlan, name: "trigger-1" }, }; useFlowStore.setState({ nodes: [conditionNode, triggerNode], edges: [], }); useFlowStore.getState().onConnect({ source: 'belief-1', target: 'trigger-1', sourceHandle: null, targetHandle: null, }); const result = TriggerReduce(triggerNode, useFlowStore.getState().nodes); expect(result).toEqual({ id: 'trigger-1', name: "trigger-1", condition: { id: "belief-1", keyword: "", }, plan: { id: expect.anything(), steps: [], },}); }); }); describe('TriggerConnects Function', () => { it('should handle connection without errors', () => { const node1: Node = { id: 'trigger-1', type: 'trigger', position: { x: 0, y: 0 }, data: { ...JSON.parse(JSON.stringify(TriggerNodeDefaults)), label: 'Trigger 1', }, }; const node2: Node = { id: 'norm-1', type: 'norm', position: { x: 100, y: 0 }, data: { ...JSON.parse(JSON.stringify(NormNodeDefaults)), label: 'Norm 1', }, }; expect(() => { TriggerConnectionSource(node1, node2.id); TriggerConnectionTarget(node1, node2.id); }).not.toThrow(); }); }); describe('TriggerConnects Function', () => { it('should correctly remove a goal from the triggers plan after it has been disconnected', () => { // first, define the goal node and trigger node. const goal: Node = { id: 'g-1', type: 'goal', position: { x: 0, y: 0 }, data: { ...JSON.parse(JSON.stringify(GoalNodeDefaults)), name: 'Goal 1' }, }; const trigger: Node = { id: 'trigger-1', type: 'trigger', position: { x: 0, y: 0 }, data: { ...JSON.parse(JSON.stringify(TriggerNodeDefaults)) }, }; // set initial store useFlowStore.setState({ nodes: [goal, trigger], edges: [] }); // then, connect the goal to the trigger. act(() => { useFlowStore.getState().onConnect({ source: 'g-1', target: 'trigger-1', sourceHandle: null, targetHandle: null }); }); // expect the goal id to be part of a goal step of the plan. let updatedTrigger = useFlowStore.getState().nodes.find((n) => n.id === 'trigger-1'); expect(updatedTrigger?.data.plan).toBeDefined(); const plan = updatedTrigger?.data.plan as any; expect(plan.steps.find((s: any) => s.id === 'g-1')).toBeDefined(); // then, disconnect the goal from the trigger. act(() => { useFlowStore.getState().onEdgesDelete([{ id: 'g-1-trigger-1', source: 'g-1', target: 'trigger-1' } as any]); }); // finally, expect the goal id to NOT be part of the goal step of the plan. updatedTrigger = useFlowStore.getState().nodes.find((n) => n.id === 'trigger-1'); const planAfter = updatedTrigger?.data.plan as any; const stillHas = planAfter?.steps?.find((s: any) => s.id === 'g-1'); expect(stillHas).toBeUndefined(); }); }); });