From a0efb97904d5b9bb3ba70357bc40603d670fa942 Mon Sep 17 00:00:00 2001 From: Nikhil Kuriakose Date: Mon, 22 Sep 2025 22:06:20 +0200 Subject: [PATCH] fix: Pass project ID to the wf execution demo iframe (#19785) --- .../WorkflowHistory/WorkflowHistoryContent.test.ts | 7 ++++++- .../src/components/WorkflowPreview.test.ts | 14 ++++++++++++-- .../editor-ui/src/components/WorkflowPreview.vue | 4 ++++ packages/frontend/editor-ui/src/views/NodeView.vue | 8 ++++++++ 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/packages/frontend/editor-ui/src/components/WorkflowHistory/WorkflowHistoryContent.test.ts b/packages/frontend/editor-ui/src/components/WorkflowHistory/WorkflowHistoryContent.test.ts index 65e771a7e16..a26fa60f73b 100644 --- a/packages/frontend/editor-ui/src/components/WorkflowHistory/WorkflowHistoryContent.test.ts +++ b/packages/frontend/editor-ui/src/components/WorkflowHistory/WorkflowHistoryContent.test.ts @@ -10,6 +10,7 @@ import type { WorkflowHistoryActionTypes } from '@n8n/rest-api-client/api/workfl import { workflowVersionDataFactory } from '@/stores/__tests__/utils/workflowHistoryTestUtils'; import type { IWorkflowDb } from '@/Interface'; import type { IUser } from 'n8n-workflow'; +import { useProjectsStore } from '@/stores/projects.store'; const actionTypes: WorkflowHistoryActionTypes = ['restore', 'clone', 'open', 'download']; const actions: Array> = actionTypes.map((value) => ({ @@ -21,12 +22,17 @@ const actions: Array> = actionTypes.map((value) => ({ const renderComponent = createComponentRenderer(WorkflowHistoryContent); let pinia: ReturnType; +let projectsStore: ReturnType; let postMessageSpy: MockInstance; describe('WorkflowHistoryContent', () => { beforeEach(() => { pinia = createPinia(); setActivePinia(pinia); + projectsStore = useProjectsStore(); + + // Mock currentProjectId for all tests + vi.spyOn(projectsStore, 'currentProjectId', 'get').mockReturnValue('test-project-id'); postMessageSpy = vi.fn(); Object.defineProperty(HTMLIFrameElement.prototype, 'contentWindow', { @@ -84,7 +90,6 @@ describe('WorkflowHistoryContent', () => { }); window.postMessage('{"command":"n8nReady"}', '*'); - await waitFor(() => { expect(postMessageSpy).toHaveBeenCalledWith(expect.not.stringContaining('pinData'), '*'); }); diff --git a/packages/frontend/editor-ui/src/components/WorkflowPreview.test.ts b/packages/frontend/editor-ui/src/components/WorkflowPreview.test.ts index f9b3e3921fa..c2aa1cebac1 100644 --- a/packages/frontend/editor-ui/src/components/WorkflowPreview.test.ts +++ b/packages/frontend/editor-ui/src/components/WorkflowPreview.test.ts @@ -6,11 +6,13 @@ import { createComponentRenderer } from '@/__tests__/render'; import type { INodeUi, IWorkflowDb } from '@/Interface'; import WorkflowPreview from '@/components/WorkflowPreview.vue'; import { useExecutionsStore } from '@/stores/executions.store'; +import { useProjectsStore } from '@/stores/projects.store'; const renderComponent = createComponentRenderer(WorkflowPreview); let pinia: ReturnType; let executionsStore: ReturnType; +let projectsStore: ReturnType; let postMessageSpy: Mock; let focusSpy: Mock; let consoleErrorSpy: MockInstance; @@ -24,6 +26,10 @@ describe('WorkflowPreview', () => { pinia = createPinia(); setActivePinia(pinia); executionsStore = useExecutionsStore(); + projectsStore = useProjectsStore(); + + // Mock currentProjectId for all tests + vi.spyOn(projectsStore, 'currentProjectId', 'get').mockReturnValue('test-project-id'); consoleErrorSpy = vi.spyOn(console, 'error'); postMessageSpy = vi.fn(); @@ -105,6 +111,7 @@ describe('WorkflowPreview', () => { workflow, canOpenNDV: true, hideNodeIssues: false, + projectId: 'test-project-id', }), '*', ); @@ -147,6 +154,7 @@ describe('WorkflowPreview', () => { executionId, executionMode: '', canOpenNDV: true, + projectId: 'test-project-id', }), '*', ); @@ -176,6 +184,7 @@ describe('WorkflowPreview', () => { executionId, executionMode: '', canOpenNDV: true, + projectId: 'test-project-id', }), '*', ); @@ -190,7 +199,7 @@ describe('WorkflowPreview', () => { }); }); - it('iframe should toggle "openNDV" class with postmessages', async () => { + it('iframe should toggle "openNDV" class with postMessages', async () => { const nodes = [{ name: 'Start' }] as INodeUi[]; const workflow = { nodes } as IWorkflowDb; const { container } = renderComponent({ @@ -213,6 +222,7 @@ describe('WorkflowPreview', () => { workflow, canOpenNDV: true, hideNodeIssues: false, + projectId: 'test-project-id', }), '*', ); @@ -249,6 +259,7 @@ describe('WorkflowPreview', () => { workflow, canOpenNDV: false, hideNodeIssues: false, + projectId: 'test-project-id', }), '*', ); @@ -260,7 +271,6 @@ describe('WorkflowPreview', () => { pinia, props: {}, }); - sendPostMessageCommand('error'); await waitFor(() => { diff --git a/packages/frontend/editor-ui/src/components/WorkflowPreview.vue b/packages/frontend/editor-ui/src/components/WorkflowPreview.vue index fc684d31949..a963f68d2ce 100644 --- a/packages/frontend/editor-ui/src/components/WorkflowPreview.vue +++ b/packages/frontend/editor-ui/src/components/WorkflowPreview.vue @@ -5,6 +5,7 @@ import { useToast } from '@/composables/useToast'; import type { IWorkflowDb } from '@/Interface'; import type { IWorkflowTemplate } from '@n8n/rest-api-client/api/templates'; import { useExecutionsStore } from '@/stores/executions.store'; +import { useProjectsStore } from '@/stores/projects.store'; const props = withDefaults( defineProps<{ @@ -40,6 +41,7 @@ const emit = defineEmits<{ const i18n = useI18n(); const toast = useToast(); const executionsStore = useExecutionsStore(); +const projectsStore = useProjectsStore(); const iframeRef = ref(null); const nodeViewDetailsOpened = ref(false); @@ -75,6 +77,7 @@ const loadWorkflow = () => { workflow: props.workflow, canOpenNDV: props.canOpenNDV, hideNodeIssues: props.hideNodeIssues, + projectId: projectsStore.currentProjectId, }), '*', ); @@ -99,6 +102,7 @@ const loadExecution = () => { executionMode: props.executionMode ?? '', nodeId: props.nodeId, canOpenNDV: props.canOpenNDV, + projectId: projectsStore.currentProjectId, }), '*', ); diff --git a/packages/frontend/editor-ui/src/views/NodeView.vue b/packages/frontend/editor-ui/src/views/NodeView.vue index 2594d01d348..2eb1735b2f0 100644 --- a/packages/frontend/editor-ui/src/views/NodeView.vue +++ b/packages/frontend/editor-ui/src/views/NodeView.vue @@ -1602,6 +1602,10 @@ async function onPostMessageReceived(messageEvent: MessageEvent) { const json = JSON.parse(messageEvent.data); if (json && json.command === 'openWorkflow') { try { + // Set the project if provided from the parent window + if (json.projectId) { + await fetchAndSetProject(json.projectId); + } await importWorkflowExact(json); canOpenNDV.value = json.canOpenNDV ?? true; hideNodeIssues.value = json.hideNodeIssues ?? false; @@ -1620,6 +1624,10 @@ async function onPostMessageReceived(messageEvent: MessageEvent) { } } else if (json && json.command === 'openExecution') { try { + // Set the project if provided from the parent window + if (json.projectId) { + await fetchAndSetProject(json.projectId); + } // If this NodeView is used in preview mode (in iframe) it will not have access to the main app store // so everything it needs has to be sent using post messages and passed down to child components isProductionExecutionPreview.value =