fix(editor): Allow executing none single webhook triggers in active workflows with multiple triggers (#16794)

This commit is contained in:
Danny Martini
2025-06-30 11:30:27 +02:00
committed by GitHub
parent eb138ecf8d
commit 725ce23ea3
2 changed files with 107 additions and 10 deletions

View File

@@ -27,6 +27,7 @@ import { usePushConnectionStore } from '@/stores/pushConnection.store';
import { createTestNode, createTestWorkflow } from '@/__tests__/mocks';
import { waitFor } from '@testing-library/vue';
import { useAgentRequestStore } from '@n8n/stores/useAgentRequestStore';
import { SLACK_TRIGGER_NODE_TYPE, MANUAL_TRIGGER_NODE_TYPE } from '@/constants';
vi.mock('@/stores/workflows.store', () => {
const storeState: Partial<ReturnType<typeof useWorkflowsStore>> & {
@@ -241,7 +242,7 @@ describe('useRunWorkflow({ router })', () => {
nodes: [
{
name: 'Slack',
type: 'n8n-nodes-base.slackTrigger',
type: SLACK_TRIGGER_NODE_TYPE,
disabled: false,
},
],
@@ -259,7 +260,7 @@ describe('useRunWorkflow({ router })', () => {
});
});
it('should execute workflow has pin data and is active with single webhook trigger', async () => {
it('should execute the workflow if the single webhook trigger has pin data', async () => {
const pinia = createTestingPinia({ stubActions: false });
setActivePinia(pinia);
const toast = useToast();
@@ -272,7 +273,7 @@ describe('useRunWorkflow({ router })', () => {
nodes: [
{
name: 'Slack',
type: 'n8n-nodes-base.slackTrigger',
type: SLACK_TRIGGER_NODE_TYPE,
disabled: false,
},
],
@@ -289,10 +290,6 @@ describe('useRunWorkflow({ router })', () => {
} as unknown as Workflow);
vi.mocked(workflowsStore).runWorkflow.mockResolvedValue(mockExecutionResponse);
vi.mocked(workflowsStore).nodesIssuesExist = true;
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue({
id: 'workflowId',
nodes: [],
} as unknown as WorkflowData);
vi.mocked(workflowsStore).getWorkflowRunData = {
NodeName: [],
};
@@ -309,6 +306,100 @@ describe('useRunWorkflow({ router })', () => {
});
});
it('should execute the workflow if there is a single webhook trigger, but another trigger is chosen', async () => {
// ARRANGE
const pinia = createTestingPinia({ stubActions: false });
setActivePinia(pinia);
const toast = useToast();
const i18n = useI18n();
const { runWorkflow } = useRunWorkflow({ router });
const mockExecutionResponse = { executionId: '123' };
const triggerNode = 'Manual';
vi.mocked(workflowsStore).isWorkflowActive = true;
vi.mocked(useWorkflowHelpers()).getWorkflowDataToSave.mockResolvedValue({
nodes: [
{
name: 'Slack',
type: SLACK_TRIGGER_NODE_TYPE,
disabled: false,
},
{
name: triggerNode,
type: MANUAL_TRIGGER_NODE_TYPE,
disabled: false,
},
],
} as unknown as WorkflowData);
vi.mocked(uiStore).activeActions = [''];
vi.mocked(workflowHelpers).getCurrentWorkflow.mockReturnValue({
name: 'Test Workflow',
} as unknown as Workflow);
vi.mocked(workflowsStore).runWorkflow.mockResolvedValue(mockExecutionResponse);
vi.mocked(workflowsStore).nodesIssuesExist = true;
vi.mocked(workflowsStore).getWorkflowRunData = { NodeName: [] };
// ACT
const result = await runWorkflow({ triggerNode });
// ASSERT
expect(result).toEqual(mockExecutionResponse);
expect(toast.showMessage).not.toHaveBeenCalledWith({
title: i18n.baseText('workflowRun.showError.deactivate'),
message: i18n.baseText('workflowRun.showError.productionActive', {
interpolate: { nodeName: 'Webhook' },
}),
type: 'error',
});
});
it('should prevent execution and show error message when workflow is active with multiple tirggers and a single webhook trigger is chosen', async () => {
// ARRANGE
const pinia = createTestingPinia({ stubActions: false });
setActivePinia(pinia);
const toast = useToast();
const i18n = useI18n();
const { runWorkflow } = useRunWorkflow({ router });
const mockExecutionResponse = { executionId: '123' };
const triggerNode = 'Slack';
vi.mocked(workflowsStore).isWorkflowActive = true;
vi.mocked(useWorkflowHelpers()).getWorkflowDataToSave.mockResolvedValue({
nodes: [
{
name: triggerNode,
type: SLACK_TRIGGER_NODE_TYPE,
disabled: false,
},
{
name: 'Manual',
type: MANUAL_TRIGGER_NODE_TYPE,
disabled: false,
},
],
} as unknown as WorkflowData);
vi.mocked(uiStore).activeActions = [''];
vi.mocked(workflowHelpers).getCurrentWorkflow.mockReturnValue({
name: 'Test Workflow',
} as unknown as Workflow);
vi.mocked(workflowsStore).runWorkflow.mockResolvedValue(mockExecutionResponse);
vi.mocked(workflowsStore).nodesIssuesExist = true;
vi.mocked(workflowsStore).getWorkflowRunData = { NodeName: [] };
// ACT
const result = await runWorkflow({ triggerNode });
// ASSERT
expect(result).toBeUndefined();
expect(toast.showMessage).toHaveBeenCalledWith({
title: i18n.baseText('workflowRun.showError.deactivate'),
message: i18n.baseText('workflowRun.showError.productionActive', {
interpolate: { nodeName: 'Webhook' },
}),
type: 'error',
});
});
it('should return undefined if UI action "workflowRunning" is active', async () => {
const { runWorkflow } = useRunWorkflow({ router });
vi.mocked(workflowsStore).setActiveExecutionId('123');

View File

@@ -276,9 +276,15 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
return true;
});
const singleWebhookTrigger = triggers.find((node) =>
SINGLE_WEBHOOK_TRIGGERS.includes(node.type),
);
const singleWebhookTrigger =
options.triggerNode === undefined
? // if there is no chosen trigger we check all triggers
triggers.find((node) => SINGLE_WEBHOOK_TRIGGERS.includes(node.type))
: // if there is a chosen trigger we check this one only
workflowData.nodes.find(
(node) =>
node.name === options.triggerNode && SINGLE_WEBHOOK_TRIGGERS.includes(node.type),
);
if (
singleWebhookTrigger &&