import { mock } from 'jest-mock-extended'; import type { INodeTypeDescription } from 'n8n-workflow'; import type { SimpleWorkflow } from '@/types'; import { evaluateTrigger } from './trigger'; describe('evaluateTrigger', () => { const mockNodeTypes: INodeTypeDescription[] = [ mock({ name: 'n8n-nodes-base.manualTrigger', displayName: 'Manual Trigger', group: ['trigger'], inputs: [], outputs: ['main'], }), mock({ name: 'n8n-nodes-base.webhookTrigger', displayName: 'Webhook Trigger', group: ['trigger'], inputs: [], outputs: ['main'], }), mock({ name: 'n8n-nodes-base.scheduleTrigger', displayName: 'Schedule Trigger', group: ['trigger'], inputs: [], outputs: ['main'], }), mock({ name: 'n8n-nodes-base.executeWorkflowTrigger', displayName: 'Execute Workflow Trigger', group: ['trigger'], inputs: [], outputs: ['main'], maxNodes: 1, }), mock({ name: 'n8n-nodes-base.code', displayName: 'Code', group: ['transform'], inputs: ['main'], outputs: ['main'], }), mock({ name: 'n8n-nodes-base.httpRequest', displayName: 'HTTP Request', group: ['transform'], inputs: ['main'], outputs: ['main'], }), mock({ name: 'n8n-nodes-base.set', displayName: 'Set', group: ['input'], inputs: ['main'], outputs: ['main'], }), ]; describe('basic trigger validation', () => { it('should return no violations for workflow with no nodes', () => { const workflow = mock({ name: 'Empty Workflow', nodes: [], connections: {}, }); const result = evaluateTrigger(workflow, mockNodeTypes); expect(result.violations).toEqual([]); }); it('should detect workflow with no trigger nodes', () => { const workflow = mock({ name: 'No Trigger Workflow', nodes: [ { id: '1', name: 'Code', type: 'n8n-nodes-base.code', parameters: {}, typeVersion: 1, position: [0, 0], }, { id: '2', name: 'HTTP Request', type: 'n8n-nodes-base.httpRequest', parameters: {}, typeVersion: 1, position: [200, 0], }, ], connections: {}, }); const result = evaluateTrigger(workflow, mockNodeTypes); expect(result.violations).toContainEqual( expect.objectContaining({ description: 'Workflow must have at least one trigger node to start execution', }), ); }); it('should accept workflow with one trigger node', () => { const workflow = mock({ name: 'Valid Workflow', nodes: [ { id: '1', name: 'Manual Trigger', type: 'n8n-nodes-base.manualTrigger', parameters: {}, typeVersion: 1, position: [0, 0], }, { id: '2', name: 'Code', type: 'n8n-nodes-base.code', parameters: {}, typeVersion: 1, position: [200, 0], }, ], connections: {}, }); const result = evaluateTrigger(workflow, mockNodeTypes); expect(result.violations).toEqual([]); }); }); describe('edge cases', () => { it('should handle unknown node types gracefully', () => { const workflow = mock({ name: 'Unknown Node Workflow', nodes: [ { id: '1', name: 'Unknown Trigger', type: 'n8n-nodes-base.unknownTrigger', parameters: {}, typeVersion: 1, position: [0, 0], }, { id: '2', name: 'Manual Trigger', type: 'n8n-nodes-base.manualTrigger', parameters: {}, typeVersion: 1, position: [200, 0], }, ], connections: {}, }); const result = evaluateTrigger(workflow, mockNodeTypes); expect(result.violations).toEqual([]); }); it('should handle mixed trigger and non-trigger nodes', () => { const workflow = mock({ name: 'Mixed Workflow', nodes: [ { id: '1', name: 'Set Data', type: 'n8n-nodes-base.set', parameters: {}, typeVersion: 1, position: [0, 0], }, { id: '2', name: 'Webhook', type: 'n8n-nodes-base.webhookTrigger', parameters: {}, typeVersion: 1, position: [200, 0], }, { id: '3', name: 'Process', type: 'n8n-nodes-base.code', parameters: {}, typeVersion: 1, position: [400, 0], }, { id: '4', name: 'Manual', type: 'n8n-nodes-base.manualTrigger', parameters: {}, typeVersion: 1, position: [0, 200], }, { id: '5', name: 'HTTP Call', type: 'n8n-nodes-base.httpRequest', parameters: {}, typeVersion: 1, position: [600, 0], }, ], connections: {}, }); const result = evaluateTrigger(workflow, mockNodeTypes); expect(result.violations).toEqual([]); }); }); });