feat(editor): Add capability to open NDV and rename node (no-changelog) (#9712)

This commit is contained in:
Alex Grozav
2024-06-17 15:46:55 +03:00
committed by GitHub
parent 87cb199745
commit 12604fe1da
8 changed files with 568 additions and 97 deletions

View File

@@ -6,14 +6,16 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
import { useUIStore } from '@/stores/ui.store';
import { useHistoryStore } from '@/stores/history.store';
import { createPinia, setActivePinia } from 'pinia';
import { createTestNode } from '@/__tests__/mocks';
import { createTestNode, createTestWorkflowObject } from '@/__tests__/mocks';
import type { Connection } from '@vue-flow/core';
import type { IConnection } from 'n8n-workflow';
import { NodeConnectionType } from 'n8n-workflow';
import { useNDVStore } from '@/stores/ndv.store';
describe('useCanvasOperations', () => {
let workflowsStore: ReturnType<typeof useWorkflowsStore>;
let uiStore: ReturnType<typeof useUIStore>;
let ndvStore: ReturnType<typeof useNDVStore>;
let historyStore: ReturnType<typeof useHistoryStore>;
let canvasOperations: ReturnType<typeof useCanvasOperations>;
@@ -23,6 +25,7 @@ describe('useCanvasOperations', () => {
workflowsStore = useWorkflowsStore();
uiStore = useUIStore();
ndvStore = useNDVStore();
historyStore = useHistoryStore();
canvasOperations = useCanvasOperations();
});
@@ -134,6 +137,93 @@ describe('useCanvasOperations', () => {
});
});
describe('renameNode', () => {
it('should rename node', async () => {
const oldName = 'Old Node';
const newName = 'New Node';
const workflowObject = createTestWorkflowObject();
workflowObject.renameNode = vi.fn();
vi.spyOn(workflowsStore, 'getCurrentWorkflow').mockReturnValue(workflowObject);
workflowsStore.getNodeByName = vi.fn().mockReturnValue({ name: oldName });
ndvStore.activeNodeName = oldName;
await canvasOperations.renameNode(oldName, newName);
expect(workflowObject.renameNode).toHaveBeenCalledWith(oldName, newName);
expect(ndvStore.activeNodeName).toBe(newName);
});
it('should not rename node when new name is same as old name', async () => {
const oldName = 'Old Node';
workflowsStore.getNodeByName = vi.fn().mockReturnValue({ name: oldName });
ndvStore.activeNodeName = oldName;
await canvasOperations.renameNode(oldName, oldName);
expect(ndvStore.activeNodeName).toBe(oldName);
});
});
describe('revertRenameNode', () => {
it('should revert node renaming', async () => {
const oldName = 'Old Node';
const currentName = 'New Node';
workflowsStore.getNodeByName = vi.fn().mockReturnValue({ name: currentName });
ndvStore.activeNodeName = currentName;
await canvasOperations.revertRenameNode(currentName, oldName);
expect(ndvStore.activeNodeName).toBe(oldName);
});
it('should not revert node renaming when old name is same as new name', async () => {
const oldName = 'Old Node';
workflowsStore.getNodeByName = vi.fn().mockReturnValue({ name: oldName });
ndvStore.activeNodeName = oldName;
await canvasOperations.revertRenameNode(oldName, oldName);
expect(ndvStore.activeNodeName).toBe(oldName);
});
});
describe('setNodeActive', () => {
it('should set active node name when node exists', () => {
const nodeId = 'node1';
const nodeName = 'Node 1';
workflowsStore.getNodeById = vi.fn().mockReturnValue({ name: nodeName });
ndvStore.activeNodeName = '';
canvasOperations.setNodeActive(nodeId);
expect(ndvStore.activeNodeName).toBe(nodeName);
});
it('should not change active node name when node does not exist', () => {
const nodeId = 'node1';
workflowsStore.getNodeById = vi.fn().mockReturnValue(undefined);
ndvStore.activeNodeName = 'Existing Node';
canvasOperations.setNodeActive(nodeId);
expect(ndvStore.activeNodeName).toBe('Existing Node');
});
});
describe('setNodeActiveByName', () => {
it('should set active node name', () => {
const nodeName = 'Node 1';
ndvStore.activeNodeName = '';
canvasOperations.setNodeActiveByName(nodeName);
expect(ndvStore.activeNodeName).toBe(nodeName);
});
});
describe('createConnection', () => {
it('should not create a connection if source node does not exist', () => {
const addConnectionSpy = vi

View File

@@ -6,15 +6,22 @@ import { useHistoryStore } from '@/stores/history.store';
import { useUIStore } from '@/stores/ui.store';
import { useTelemetry } from '@/composables/useTelemetry';
import { useExternalHooks } from '@/composables/useExternalHooks';
import { MoveNodeCommand, RemoveConnectionCommand, RemoveNodeCommand } from '@/models/history';
import {
MoveNodeCommand,
RemoveConnectionCommand,
RemoveNodeCommand,
RenameNodeCommand,
} from '@/models/history';
import type { Connection } from '@vue-flow/core';
import { mapCanvasConnectionToLegacyConnection } from '@/utils/canvasUtilsV2';
import { getUniqueNodeName, mapCanvasConnectionToLegacyConnection } from '@/utils/canvasUtilsV2';
import type { IConnection } from 'n8n-workflow';
import { useNDVStore } from '@/stores/ndv.store';
export function useCanvasOperations() {
const workflowsStore = useWorkflowsStore();
const historyStore = useHistoryStore();
const uiStore = useUIStore();
const ndvStore = useNDVStore();
const telemetry = useTelemetry();
const externalHooks = useExternalHooks();
@@ -51,6 +58,45 @@ export function useCanvasOperations() {
}
}
async function renameNode(currentName: string, newName: string, { trackHistory = false } = {}) {
if (currentName === newName) {
return;
}
if (trackHistory) {
historyStore.startRecordingUndo();
}
newName = getUniqueNodeName(newName, workflowsStore.canvasNames);
// Rename the node and update the connections
const workflow = workflowsStore.getCurrentWorkflow(true);
workflow.renameNode(currentName, newName);
if (trackHistory) {
historyStore.pushCommandToUndo(new RenameNodeCommand(currentName, newName));
}
// Update also last selected node and execution data
workflowsStore.renameNodeSelectedAndExecution({ old: currentName, new: newName });
workflowsStore.setNodes(Object.values(workflow.nodes));
workflowsStore.setConnections(workflow.connectionsBySourceNode);
const isRenamingActiveNode = ndvStore.activeNodeName === currentName;
if (isRenamingActiveNode) {
ndvStore.activeNodeName = newName;
}
if (trackHistory) {
historyStore.stopRecordingUndo();
}
}
async function revertRenameNode(currentName: string, previousName: string) {
await renameNode(currentName, previousName);
}
function deleteNode(id: string, { trackHistory = false, trackBulk = true } = {}) {
const node = workflowsStore.getNodeById(id);
if (!node) {
@@ -100,6 +146,19 @@ export function useCanvasOperations() {
}
}
function setNodeActive(id: string) {
const node = workflowsStore.getNodeById(id);
if (!node) {
return;
}
ndvStore.activeNodeName = node.name;
}
function setNodeActiveByName(name: string) {
ndvStore.activeNodeName = name;
}
/**
* Connection operations
*/
@@ -204,6 +263,10 @@ export function useCanvasOperations() {
return {
updateNodePosition,
setNodeActive,
setNodeActiveByName,
renameNode,
revertRenameNode,
deleteNode,
revertDeleteNode,
trackDeleteNode,