mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
feat(editor): Provide default ExecuteWorkflow node names based on the selected workflow (#18953)
This commit is contained in:
@@ -92,6 +92,7 @@ const {
|
||||
searchFilter,
|
||||
onSearchFilter,
|
||||
getWorkflowName,
|
||||
renameDefaultNodeName,
|
||||
populateNextWorkflowsPage,
|
||||
setWorkflowsResources,
|
||||
reloadWorkflows,
|
||||
@@ -174,6 +175,10 @@ function onListItemSelected(value: NodeParameterValue) {
|
||||
telemetry.track('User chose sub-workflow', {});
|
||||
onInputChange(value);
|
||||
hideDropdown();
|
||||
// we rename defaults here to allow selecting the same workflow to
|
||||
// update the name, as we don't eagerly update a changed workflow name
|
||||
// but rather only react on changed id elsewhere
|
||||
renameDefaultNodeName(value);
|
||||
}
|
||||
|
||||
function onInputFocus(): void {
|
||||
@@ -239,6 +244,19 @@ watch(
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val, old) => {
|
||||
// We update the name only if the actual ID changed
|
||||
// Because eagerly renaming the node when the target sub-workflow
|
||||
// changed name means the workflow becomes unsaved and changed just by
|
||||
// opening the ExecuteWorkflow node referencing the renamed workflow
|
||||
if (old.value !== val.value) {
|
||||
renameDefaultNodeName(val.value);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onClickOutside(dropdown, () => {
|
||||
isDropdownVisible.value = false;
|
||||
});
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { useWorkflowResourcesLocator } from './useWorkflowResourcesLocator';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { type MockedStore, mockedStore } from '@/__tests__/utils';
|
||||
import type { IWorkflowDb } from '@/Interface';
|
||||
import type { Router } from 'vue-router';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
|
||||
const useCanvasOperations = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock('@/composables/useCanvasOperations', () => ({
|
||||
useCanvasOperations,
|
||||
}));
|
||||
|
||||
describe('useWorkflowResourcesLocator', () => {
|
||||
let workflowsStoreMock: MockedStore<typeof useWorkflowsStore>;
|
||||
let ndvStoreMock: MockedStore<typeof useNDVStore>;
|
||||
|
||||
const renameNodeMock = vi.fn();
|
||||
const routerMock = { resolve: vi.fn() } as unknown as Router;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
createTestingPinia();
|
||||
workflowsStoreMock = mockedStore(useWorkflowsStore);
|
||||
ndvStoreMock = mockedStore(useNDVStore);
|
||||
|
||||
useCanvasOperations.mockReturnValue({ renameNode: renameNodeMock });
|
||||
});
|
||||
|
||||
describe('renameDefaultNodeName', () => {
|
||||
it.each([
|
||||
{
|
||||
activeNodeName: 'Execute Workflow',
|
||||
workflowId: 'workflow-id',
|
||||
mockedWorkflow: { name: 'Test Workflow' },
|
||||
expectedRename: "Call 'Test Workflow'",
|
||||
expectedCalledWith: 'Execute Workflow',
|
||||
},
|
||||
{
|
||||
activeNodeName: 'Call n8n Workflow Tool',
|
||||
workflowId: 'workflow-id',
|
||||
mockedWorkflow: { name: 'Test Workflow' },
|
||||
expectedRename: "Call 'Test Workflow'",
|
||||
expectedCalledWith: 'Call n8n Workflow Tool',
|
||||
},
|
||||
{
|
||||
activeNodeName: "Call 'Old Workflow'",
|
||||
workflowId: 'workflow-id',
|
||||
mockedWorkflow: { name: 'New Workflow' },
|
||||
expectedRename: "Call 'New Workflow'",
|
||||
expectedCalledWith: "Call 'Old Workflow'",
|
||||
},
|
||||
])(
|
||||
'should rename the node correctly for activeNodeName: $activeNodeName',
|
||||
({ activeNodeName, workflowId, mockedWorkflow, expectedRename, expectedCalledWith }) => {
|
||||
const { renameDefaultNodeName } = useWorkflowResourcesLocator(routerMock);
|
||||
|
||||
ndvStoreMock.activeNodeName = activeNodeName;
|
||||
workflowsStoreMock.getWorkflowById.mockReturnValue(
|
||||
mockedWorkflow as unknown as IWorkflowDb,
|
||||
);
|
||||
|
||||
renameDefaultNodeName(workflowId);
|
||||
|
||||
expect(workflowsStoreMock.getWorkflowById).toHaveBeenCalledWith(workflowId);
|
||||
expect(renameNodeMock).toHaveBeenCalledWith(expectedCalledWith, expectedRename);
|
||||
},
|
||||
);
|
||||
|
||||
it('should not rename the node for invalid workflowId', () => {
|
||||
const { renameDefaultNodeName } = useWorkflowResourcesLocator(routerMock);
|
||||
const workflowId = 123;
|
||||
|
||||
renameDefaultNodeName(workflowId);
|
||||
|
||||
expect(renameNodeMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not rename the node for workflowId: workflow-id with null mockedWorkflow', () => {
|
||||
const { renameDefaultNodeName } = useWorkflowResourcesLocator(routerMock);
|
||||
const workflowId = 'workflow-id';
|
||||
const activeNodeName = 'Execute Workflow';
|
||||
|
||||
ndvStoreMock.activeNodeName = activeNodeName;
|
||||
workflowsStoreMock.getWorkflowById.mockReturnValue(null as unknown as IWorkflowDb);
|
||||
|
||||
renameDefaultNodeName(workflowId);
|
||||
|
||||
expect(workflowsStoreMock.getWorkflowById).toHaveBeenCalledWith(workflowId);
|
||||
expect(renameNodeMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not rename the node for workflowId: workflow-id with activeNodeName: Some Other Node', () => {
|
||||
const { renameDefaultNodeName } = useWorkflowResourcesLocator(routerMock);
|
||||
const workflowId = 'workflow-id';
|
||||
const activeNodeName = 'Some Other Node';
|
||||
const mockedWorkflow = { name: 'Test Workflow' };
|
||||
|
||||
ndvStoreMock.activeNodeName = activeNodeName;
|
||||
workflowsStoreMock.getWorkflowById.mockReturnValue(mockedWorkflow as unknown as IWorkflowDb);
|
||||
|
||||
renameDefaultNodeName(workflowId);
|
||||
|
||||
expect(workflowsStoreMock.getWorkflowById).not.toHaveBeenCalled();
|
||||
expect(renameNodeMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -5,9 +5,15 @@ import type { Router } from 'vue-router';
|
||||
import { VIEWS } from '@/constants';
|
||||
|
||||
import type { IWorkflowDb } from '@/Interface';
|
||||
import type { NodeParameterValue } from 'n8n-workflow';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useCanvasOperations } from '@/composables/useCanvasOperations';
|
||||
|
||||
export function useWorkflowResourcesLocator(router: Router) {
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const ndvStore = useNDVStore();
|
||||
const { renameNode } = useCanvasOperations();
|
||||
|
||||
const workflowsResources = ref<Array<{ name: string; value: string; url: string }>>([]);
|
||||
const isLoadingResources = ref(true);
|
||||
const searchFilter = ref('');
|
||||
@@ -77,10 +83,34 @@ export function useWorkflowResourcesLocator(router: Router) {
|
||||
return id;
|
||||
}
|
||||
|
||||
function getWorkflowBaseName(id: string): string | null {
|
||||
const workflow = workflowsStore.getWorkflowById(id);
|
||||
if (workflow) {
|
||||
return workflow.name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function onSearchFilter(filter: string) {
|
||||
searchFilter.value = filter;
|
||||
}
|
||||
|
||||
function renameDefaultNodeName(workflowId: NodeParameterValue) {
|
||||
if (typeof workflowId !== 'string') return;
|
||||
|
||||
const nodeName = ndvStore.activeNodeName;
|
||||
if (
|
||||
nodeName === 'Execute Workflow' ||
|
||||
nodeName === 'Call n8n Workflow Tool' ||
|
||||
(nodeName?.startsWith("Call '") && nodeName?.endsWith("'"))
|
||||
) {
|
||||
const baseName = getWorkflowBaseName(workflowId);
|
||||
if (baseName !== null) {
|
||||
void renameNode(nodeName, `Call '${baseName}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
workflowsResources,
|
||||
isLoadingResources,
|
||||
@@ -91,6 +121,7 @@ export function useWorkflowResourcesLocator(router: Router) {
|
||||
getWorkflowUrl,
|
||||
onSearchFilter,
|
||||
getWorkflowName,
|
||||
renameDefaultNodeName,
|
||||
populateNextWorkflowsPage,
|
||||
setWorkflowsResources,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user