import React, { useCallback } from 'react' import { act, render } from '@testing-library/react' import { useTriggerStatusStore } from '../store/trigger-status' import { isTriggerNode } from '../types' import type { BlockEnum } from '../types' import type { EntryNodeStatus } from '../store/trigger-status' // Mock the isTriggerNode function while preserving BlockEnum jest.mock('../types', () => ({ ...jest.requireActual('../types'), isTriggerNode: jest.fn(), })) const mockIsTriggerNode = isTriggerNode as jest.MockedFunction // Test component that mimics BaseNode's usage pattern const TestTriggerNode: React.FC<{ nodeId: string nodeType: string }> = ({ nodeId, nodeType }) => { const triggerStatus = useTriggerStatusStore(state => mockIsTriggerNode(nodeType as BlockEnum) ? (state.triggerStatuses[nodeId] || 'disabled') : 'enabled', ) return (
Status: {triggerStatus}
) } // Test component that mimics TriggerCard's usage pattern const TestTriggerController: React.FC = () => { const { setTriggerStatus, setTriggerStatuses } = useTriggerStatusStore() const handleToggle = (nodeId: string, enabled: boolean) => { const newStatus = enabled ? 'enabled' : 'disabled' setTriggerStatus(nodeId, newStatus) } const handleBatchUpdate = (statuses: Record) => { setTriggerStatuses(statuses) } return (
) } describe('Trigger Status Synchronization Integration', () => { beforeEach(() => { // Clear store state act(() => { const store = useTriggerStatusStore.getState() store.clearTriggerStatuses() }) // Reset mocks jest.clearAllMocks() }) describe('Real-time Status Synchronization', () => { it('should sync status changes between trigger controller and nodes', () => { mockIsTriggerNode.mockReturnValue(true) const { getByTestId } = render( <> , ) // Initial state - should be 'disabled' by default expect(getByTestId('node-node-1')).toHaveAttribute('data-status', 'disabled') expect(getByTestId('node-node-2')).toHaveAttribute('data-status', 'disabled') // Enable node-1 act(() => { getByTestId('toggle-node-1').click() }) expect(getByTestId('node-node-1')).toHaveAttribute('data-status', 'enabled') expect(getByTestId('node-node-2')).toHaveAttribute('data-status', 'disabled') // Disable node-2 (should remain disabled) act(() => { getByTestId('toggle-node-2').click() }) expect(getByTestId('node-node-1')).toHaveAttribute('data-status', 'enabled') expect(getByTestId('node-node-2')).toHaveAttribute('data-status', 'disabled') }) it('should handle batch status updates correctly', () => { mockIsTriggerNode.mockReturnValue(true) const { getByTestId } = render( <> , ) // Initial state expect(getByTestId('node-node-1')).toHaveAttribute('data-status', 'disabled') expect(getByTestId('node-node-2')).toHaveAttribute('data-status', 'disabled') expect(getByTestId('node-node-3')).toHaveAttribute('data-status', 'disabled') // Batch update act(() => { getByTestId('batch-update').click() }) expect(getByTestId('node-node-1')).toHaveAttribute('data-status', 'disabled') expect(getByTestId('node-node-2')).toHaveAttribute('data-status', 'enabled') expect(getByTestId('node-node-3')).toHaveAttribute('data-status', 'enabled') }) it('should handle mixed node types (trigger vs non-trigger)', () => { // Mock different node types mockIsTriggerNode.mockImplementation((nodeType: string) => { return nodeType.startsWith('trigger-') }) const { getByTestId } = render( <> , ) // Trigger node should use store status, non-trigger nodes should be 'enabled' expect(getByTestId('node-node-1')).toHaveAttribute('data-status', 'disabled') // trigger node expect(getByTestId('node-node-2')).toHaveAttribute('data-status', 'enabled') // start node expect(getByTestId('node-node-3')).toHaveAttribute('data-status', 'enabled') // llm node // Update trigger node status act(() => { getByTestId('toggle-node-1').click() }) expect(getByTestId('node-node-1')).toHaveAttribute('data-status', 'enabled') // updated expect(getByTestId('node-node-2')).toHaveAttribute('data-status', 'enabled') // unchanged expect(getByTestId('node-node-3')).toHaveAttribute('data-status', 'enabled') // unchanged }) }) describe('Store State Management', () => { it('should maintain state consistency across multiple components', () => { mockIsTriggerNode.mockReturnValue(true) // Render multiple instances of the same node const { getByTestId, rerender } = render( <> , ) // Update status act(() => { getByTestId('toggle-node-1').click() // This updates node-1, not shared-node }) // Add another component with the same nodeId rerender( <> , ) // Both components should show the same status const nodes = document.querySelectorAll('[data-testid="node-shared-node"]') expect(nodes).toHaveLength(2) nodes.forEach((node) => { expect(node).toHaveAttribute('data-status', 'disabled') }) }) it('should handle rapid status changes correctly', () => { mockIsTriggerNode.mockReturnValue(true) const { getByTestId } = render( <> , ) // Rapid consecutive updates act(() => { // Multiple rapid clicks getByTestId('toggle-node-1').click() // enable getByTestId('toggle-node-2').click() // disable (different node) getByTestId('toggle-node-1').click() // enable again }) // Should reflect the final state expect(getByTestId('node-node-1')).toHaveAttribute('data-status', 'enabled') }) }) describe('Error Scenarios', () => { it('should handle non-existent node IDs gracefully', () => { mockIsTriggerNode.mockReturnValue(true) const { getByTestId } = render( , ) // Should default to 'disabled' for non-existent nodes expect(getByTestId('node-non-existent-node')).toHaveAttribute('data-status', 'disabled') }) it('should handle component unmounting gracefully', () => { mockIsTriggerNode.mockReturnValue(true) const { getByTestId, unmount } = render( <> , ) // Update status act(() => { getByTestId('toggle-node-1').click() }) // Unmount components expect(() => unmount()).not.toThrow() // Store should still maintain the state const store = useTriggerStatusStore.getState() expect(store.triggerStatuses['node-1']).toBe('enabled') }) }) describe('Performance Optimization', () => { // Component that uses optimized selector with useCallback const OptimizedTriggerNode: React.FC<{ nodeId: string nodeType: string }> = ({ nodeId, nodeType }) => { const triggerStatusSelector = useCallback((state: any) => mockIsTriggerNode(nodeType as BlockEnum) ? (state.triggerStatuses[nodeId] || 'disabled') : 'enabled', [nodeId, nodeType], ) const triggerStatus = useTriggerStatusStore(triggerStatusSelector) return (
Status: {triggerStatus}
) } it('should work correctly with optimized selector using useCallback', () => { mockIsTriggerNode.mockImplementation(nodeType => nodeType === 'trigger-webhook') const { getByTestId } = render( <> , ) // Initial state expect(getByTestId('optimized-node-node-1')).toHaveAttribute('data-status', 'disabled') expect(getByTestId('optimized-node-node-2')).toHaveAttribute('data-status', 'enabled') // Update status via controller act(() => { getByTestId('toggle-node-1').click() }) // Verify optimized component updates correctly expect(getByTestId('optimized-node-node-1')).toHaveAttribute('data-status', 'enabled') expect(getByTestId('optimized-node-node-2')).toHaveAttribute('data-status', 'enabled') }) it('should handle selector dependency changes correctly', () => { mockIsTriggerNode.mockImplementation(nodeType => nodeType === 'trigger-webhook') const TestComponent: React.FC<{ nodeType: string }> = ({ nodeType }) => { const triggerStatusSelector = useCallback((state: any) => mockIsTriggerNode(nodeType as BlockEnum) ? (state.triggerStatuses['test-node'] || 'disabled') : 'enabled', ['test-node', nodeType], // Dependencies should match implementation ) const status = useTriggerStatusStore(triggerStatusSelector) return
} const { getByTestId, rerender } = render() // Initial trigger node expect(getByTestId('test-component')).toHaveAttribute('data-status', 'disabled') // Set status for the node act(() => { useTriggerStatusStore.getState().setTriggerStatus('test-node', 'enabled') }) expect(getByTestId('test-component')).toHaveAttribute('data-status', 'enabled') // Change node type to non-trigger - should return 'enabled' regardless of store rerender() expect(getByTestId('test-component')).toHaveAttribute('data-status', 'enabled') }) }) })