fix: Pass project ID to the wf execution demo iframe (#19785)

This commit is contained in:
Nikhil Kuriakose 2025-09-22 22:06:20 +02:00 committed by GitHub
parent 24f08e12f0
commit a0efb97904
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 30 additions and 3 deletions

View File

@ -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<UserAction<IUser>> = actionTypes.map((value) => ({
@ -21,12 +22,17 @@ const actions: Array<UserAction<IUser>> = actionTypes.map((value) => ({
const renderComponent = createComponentRenderer(WorkflowHistoryContent);
let pinia: ReturnType<typeof createPinia>;
let projectsStore: ReturnType<typeof useProjectsStore>;
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'), '*');
});

View File

@ -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<typeof createPinia>;
let executionsStore: ReturnType<typeof useExecutionsStore>;
let projectsStore: ReturnType<typeof useProjectsStore>;
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(() => {

View File

@ -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<HTMLIFrameElement | null>(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,
}),
'*',
);

View File

@ -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 =