diff --git a/cypress/constants.ts b/cypress/constants.ts index 9711a7fc0294e..551c244102560 100644 --- a/cypress/constants.ts +++ b/cypress/constants.ts @@ -57,6 +57,7 @@ export const AI_TOOL_CODE_NODE_NAME = 'Custom Code Tool'; export const AI_TOOL_WIKIPEDIA_NODE_NAME = 'Wikipedia'; export const AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME = 'OpenAI Chat Model'; export const AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME = 'Auto-fixing Output Parser'; +export const WEBHOOK_NODE_NAME = 'Webhook'; export const META_KEY = Cypress.platform === 'darwin' ? '{meta}' : '{ctrl}'; diff --git a/cypress/e2e/7-workflow-actions.cy.ts b/cypress/e2e/7-workflow-actions.cy.ts index 794e2ee605821..754650df8b587 100644 --- a/cypress/e2e/7-workflow-actions.cy.ts +++ b/cypress/e2e/7-workflow-actions.cy.ts @@ -6,6 +6,7 @@ import { EDIT_FIELDS_SET_NODE_NAME, INSTANCE_MEMBERS, INSTANCE_OWNER, + WEBHOOK_NODE_NAME, } from '../constants'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows'; diff --git a/packages/editor-ui/src/components/DuplicateWorkflowDialog.vue b/packages/editor-ui/src/components/DuplicateWorkflowDialog.vue index da5be5994aa54..a2c60236f6d56 100644 --- a/packages/editor-ui/src/components/DuplicateWorkflowDialog.vue +++ b/packages/editor-ui/src/components/DuplicateWorkflowDialog.vue @@ -89,11 +89,6 @@ export default defineComponent({ prevTagIds: currentTagIds, }; }, - async mounted() { - this.name = await this.workflowsStore.getDuplicateCurrentWorkflowName(this.data.name); - await this.$nextTick(); - this.focusOnNameInput(); - }, computed: { ...mapStores(useCredentialsStore, useUsersStore, useSettingsStore, useWorkflowsStore), }, @@ -104,6 +99,11 @@ export default defineComponent({ } }, }, + async mounted() { + this.name = await this.workflowsStore.getDuplicateCurrentWorkflowName(this.data.name); + await this.$nextTick(); + this.focusOnNameInput(); + }, methods: { focusOnSelect() { this.dropdownBus.emit('focus'); diff --git a/packages/editor-ui/src/composables/useWorkflowHelpers.test.ts b/packages/editor-ui/src/composables/useWorkflowHelpers.test.ts new file mode 100644 index 0000000000000..81643f52aac84 --- /dev/null +++ b/packages/editor-ui/src/composables/useWorkflowHelpers.test.ts @@ -0,0 +1,123 @@ +import type { IWorkflowDataUpdate } from '@/Interface'; +import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers'; +import router from '@/router'; +import { createTestingPinia } from '@pinia/testing'; +import { setActivePinia } from 'pinia'; + +const getDuplicateTestWorkflow = (): IWorkflowDataUpdate => ({ + name: 'Duplicate webhook test', + active: false, + nodes: [ + { + parameters: { + path: '5340ae49-2c96-4492-9073-7744d2e52b8a', + options: {}, + }, + id: 'c1e1b6e7-df13-41b1-95f6-42903b85e438', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [680, 20], + webhookId: '5340ae49-2c96-4492-9073-7744d2e52b8a', + }, + { + parameters: { + path: 'aa5150d8-1d7d-4247-88d8-44c96fe3a37b', + options: {}, + }, + id: 'aa5150d8-1d7d-4247-88d8-44c96fe3a37b', + name: 'Webhook 2', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [700, 40], + webhookId: 'aa5150d8-1d7d-4247-88d8-44c96fe3a37b', + }, + { + parameters: { + resume: 'webhook', + options: { + webhookSuffix: '/test', + }, + }, + id: '979d8443-51b1-48e2-b239-acf399b66509', + name: 'Wait', + type: 'n8n-nodes-base.wait', + typeVersion: 1.1, + position: [900, 20], + webhookId: '5340ae49-2c96-4492-9073-7744d2e52b8a', + }, + ], + connections: {}, +}); + +vi.mock('@/stores/workflows.store', () => ({ + useWorkflowsStore: vi.fn(() => ({ + workflowsById: {}, + createNewWorkflow: vi.fn(() => {}), + addWorkflow: vi.fn(() => {}), + setActive: vi.fn(() => {}), + setWorkflowId: vi.fn(() => {}), + setWorkflowVersionId: vi.fn(() => {}), + setWorkflowName: vi.fn(() => {}), + setWorkflowSettings: vi.fn(() => {}), + setNodeValue: vi.fn(() => {}), + setWorkflowTagIds: vi.fn(() => {}), + getCurrentWorkflow: vi.fn(() => ({})), + })), +})); + +describe('useWorkflowHelpers', () => { + describe('saveAsNewWorkflow', () => { + beforeAll(() => { + setActivePinia(createTestingPinia()); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should respect `resetWebhookUrls: false` when duplicating workflows', async () => { + const workflow = getDuplicateTestWorkflow(); + if (!workflow.nodes) { + throw new Error('Missing nodes in test workflow'); + } + const { saveAsNewWorkflow } = useWorkflowHelpers({ router }); + const webHookIdsPreSave = workflow.nodes.map((node) => node.webhookId); + const pathsPreSave = workflow.nodes.map((node) => node.parameters.path); + + await saveAsNewWorkflow({ + name: workflow.name, + resetWebhookUrls: false, + data: workflow, + }); + + const webHookIdsPostSave = workflow.nodes.map((node) => node.webhookId); + const pathsPostSave = workflow.nodes.map((node) => node.parameters.path); + // Expect webhookIds and paths to be the same as in the original workflow + expect(webHookIdsPreSave).toEqual(webHookIdsPostSave); + expect(pathsPreSave).toEqual(pathsPostSave); + }); + + it('should respect `resetWebhookUrls: true` when duplicating workflows', async () => { + const workflow = getDuplicateTestWorkflow(); + if (!workflow.nodes) { + throw new Error('Missing nodes in test workflow'); + } + const { saveAsNewWorkflow } = useWorkflowHelpers({ router }); + const webHookIdsPreSave = workflow.nodes.map((node) => node.webhookId); + const pathsPreSave = workflow.nodes.map((node) => node.parameters.path); + + await saveAsNewWorkflow({ + name: workflow.name, + resetWebhookUrls: true, + data: workflow, + }); + + const webHookIdsPostSave = workflow.nodes.map((node) => node.webhookId); + const pathsPostSave = workflow.nodes.map((node) => node.parameters.path); + // Now, expect webhookIds and paths to be different + expect(webHookIdsPreSave).not.toEqual(webHookIdsPostSave); + expect(pathsPreSave).not.toEqual(pathsPostSave); + }); + }); +}); diff --git a/packages/editor-ui/src/composables/useWorkflowHelpers.ts b/packages/editor-ui/src/composables/useWorkflowHelpers.ts index ee75c6d8ca51f..1924ea43ff40e 100644 --- a/packages/editor-ui/src/composables/useWorkflowHelpers.ts +++ b/packages/editor-ui/src/composables/useWorkflowHelpers.ts @@ -1004,7 +1004,9 @@ export function useWorkflowHelpers(options: { router: ReturnType { if (node.webhookId) { - node.webhookId = uuid(); + const newId = uuid(); + node.webhookId = newId; + node.parameters.path = newId; changedNodes[node.name] = node.webhookId; } return node;