mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
feat(editor): Improve manual description in nodes as tools (#15373)
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import type { INodeType } from '@/index';
|
||||
import {
|
||||
NodeConnectionTypes,
|
||||
type NodeConnectionType,
|
||||
@@ -14,6 +15,9 @@ import {
|
||||
isTriggerNode,
|
||||
isExecutable,
|
||||
displayParameter,
|
||||
makeDescription,
|
||||
getUpdatedToolDescription,
|
||||
getToolDescriptionForNode,
|
||||
} from '@/NodeHelpers';
|
||||
import type { Workflow } from '@/Workflow';
|
||||
|
||||
@@ -4774,4 +4778,472 @@ describe('NodeHelpers', () => {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('makeDescription', () => {
|
||||
let mockNodeTypeDescription: INodeTypeDescription;
|
||||
|
||||
beforeEach(() => {
|
||||
// Arrange a basic mock node type description
|
||||
mockNodeTypeDescription = {
|
||||
displayName: 'Test Node',
|
||||
name: 'testNode',
|
||||
icon: 'fa:test',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
description: 'This is a test node',
|
||||
defaults: {
|
||||
name: 'Test Node',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
properties: [],
|
||||
};
|
||||
});
|
||||
|
||||
test('should return action-based description when action is available', () => {
|
||||
// Arrange
|
||||
const nodeParameters: INodeParameters = {
|
||||
resource: 'user',
|
||||
operation: 'create',
|
||||
};
|
||||
|
||||
mockNodeTypeDescription.properties = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
action: 'Create a new user',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = makeDescription(nodeParameters, mockNodeTypeDescription);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('Create a new user in Test Node');
|
||||
});
|
||||
|
||||
test('should return resource-operation-based description when action is not available', () => {
|
||||
// Arrange
|
||||
const nodeParameters: INodeParameters = {
|
||||
resource: 'user',
|
||||
operation: 'create',
|
||||
};
|
||||
|
||||
mockNodeTypeDescription.properties = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
// No action property
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = makeDescription(nodeParameters, mockNodeTypeDescription);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('create user in Test Node');
|
||||
});
|
||||
|
||||
test('should return default description when resource or operation is missing', () => {
|
||||
// Arrange
|
||||
const nodeParameters: INodeParameters = {
|
||||
// No resource or operation
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = makeDescription(nodeParameters, mockNodeTypeDescription);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('This is a test node');
|
||||
});
|
||||
|
||||
test('should handle case where nodeTypeOperation is not found', () => {
|
||||
// Arrange
|
||||
const nodeParameters: INodeParameters = {
|
||||
resource: 'user',
|
||||
operation: 'create',
|
||||
};
|
||||
|
||||
mockNodeTypeDescription.properties = [
|
||||
// No matching operation property
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = makeDescription(nodeParameters, mockNodeTypeDescription);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('create user in Test Node');
|
||||
});
|
||||
|
||||
test('should handle case where options are not a list of INodePropertyOptions', () => {
|
||||
// Arrange
|
||||
const nodeParameters: INodeParameters = {
|
||||
resource: 'user',
|
||||
operation: 'create',
|
||||
};
|
||||
|
||||
mockNodeTypeDescription.properties = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
},
|
||||
},
|
||||
// Options are not INodePropertyOptions[]
|
||||
options: [
|
||||
//@ts-expect-error
|
||||
{},
|
||||
],
|
||||
default: 'create',
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = makeDescription(nodeParameters, mockNodeTypeDescription);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('create user in Test Node');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getUpdatedToolDescription', () => {
|
||||
let mockNodeTypeDescription: INodeTypeDescription;
|
||||
|
||||
beforeEach(() => {
|
||||
// Arrange a basic mock node type description
|
||||
mockNodeTypeDescription = {
|
||||
displayName: 'Test Node',
|
||||
name: 'testNode',
|
||||
icon: 'fa:test',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
description: 'This is a test node',
|
||||
defaults: {
|
||||
name: 'Test Node',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
properties: [],
|
||||
usableAsTool: true,
|
||||
};
|
||||
});
|
||||
|
||||
test('should return undefined when descriptionType is not manual', () => {
|
||||
// Arrange
|
||||
const newParameters: INodeParameters = {
|
||||
descriptionType: 'automatic',
|
||||
resource: 'user',
|
||||
operation: 'create',
|
||||
};
|
||||
const currentParameters: INodeParameters = {
|
||||
descriptionType: 'automatic',
|
||||
resource: 'user',
|
||||
operation: 'create',
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = getUpdatedToolDescription(
|
||||
mockNodeTypeDescription,
|
||||
newParameters,
|
||||
currentParameters,
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should return new description when toolDescription matches previous description', () => {
|
||||
// Arrange
|
||||
mockNodeTypeDescription.properties = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
action: 'Create a new user',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
},
|
||||
];
|
||||
|
||||
const currentParameters: INodeParameters = {
|
||||
descriptionType: 'manual',
|
||||
resource: 'user',
|
||||
operation: 'create',
|
||||
};
|
||||
|
||||
const newParameters: INodeParameters = {
|
||||
descriptionType: 'manual',
|
||||
resource: 'user',
|
||||
operation: 'update',
|
||||
toolDescription: 'Create a new user in Test Node', // Matches the previous description
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = getUpdatedToolDescription(
|
||||
mockNodeTypeDescription,
|
||||
newParameters,
|
||||
currentParameters,
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('update user in Test Node');
|
||||
});
|
||||
|
||||
test('should return new description when toolDescription matches node type description', () => {
|
||||
// Arrange
|
||||
const currentParameters: INodeParameters = {
|
||||
descriptionType: 'manual',
|
||||
resource: 'user',
|
||||
operation: 'create',
|
||||
};
|
||||
|
||||
const newParameters: INodeParameters = {
|
||||
descriptionType: 'manual',
|
||||
resource: 'user',
|
||||
operation: 'update',
|
||||
toolDescription: 'This is a test node', // Matches the node type description
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = getUpdatedToolDescription(
|
||||
mockNodeTypeDescription,
|
||||
newParameters,
|
||||
currentParameters,
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('update user in Test Node');
|
||||
});
|
||||
|
||||
test('should return undefined when toolDescription is custom', () => {
|
||||
// Arrange
|
||||
const currentParameters: INodeParameters = {
|
||||
descriptionType: 'manual',
|
||||
resource: 'user',
|
||||
operation: 'create',
|
||||
};
|
||||
|
||||
const newParameters: INodeParameters = {
|
||||
descriptionType: 'manual',
|
||||
resource: 'user',
|
||||
operation: 'update',
|
||||
toolDescription: 'My custom description', // Custom description
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = getUpdatedToolDescription(
|
||||
mockNodeTypeDescription,
|
||||
newParameters,
|
||||
currentParameters,
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should return undefined for null inputs', () => {
|
||||
// Act
|
||||
const result = getUpdatedToolDescription(null, null);
|
||||
|
||||
// Assert
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should return new description when toolDescription is empty or whitespace', () => {
|
||||
// Arrange
|
||||
const currentParameters: INodeParameters = {
|
||||
descriptionType: 'manual',
|
||||
resource: 'user',
|
||||
operation: 'create',
|
||||
};
|
||||
|
||||
const newParameters: INodeParameters = {
|
||||
descriptionType: 'manual',
|
||||
resource: 'user',
|
||||
operation: 'update',
|
||||
toolDescription: ' ', // Empty/whitespace description
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = getUpdatedToolDescription(
|
||||
mockNodeTypeDescription,
|
||||
newParameters,
|
||||
currentParameters,
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('update user in Test Node');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getToolDescriptionForNode', () => {
|
||||
let mockNode: INode;
|
||||
let mockNodeType: INodeType;
|
||||
|
||||
beforeEach(() => {
|
||||
// Arrange a basic mock node
|
||||
mockNode = {
|
||||
id: 'test-node-id',
|
||||
name: 'Test Node',
|
||||
typeVersion: 1,
|
||||
type: 'test-node-type',
|
||||
position: [0, 0],
|
||||
parameters: {},
|
||||
};
|
||||
|
||||
// Arrange a basic mock node type
|
||||
mockNodeType = {
|
||||
description: {
|
||||
displayName: 'Test Node Type',
|
||||
name: 'testNodeType',
|
||||
icon: 'fa:test',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
description: 'This is the default node description',
|
||||
defaults: {
|
||||
name: 'Test Node Type',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
properties: [],
|
||||
},
|
||||
} as INodeType;
|
||||
});
|
||||
|
||||
test('should use generated description when descriptionType is auto', () => {
|
||||
// Arrange
|
||||
mockNode.parameters = {
|
||||
descriptionType: 'auto',
|
||||
resource: 'user',
|
||||
operation: 'create',
|
||||
};
|
||||
|
||||
mockNodeType.description.properties = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
action: 'Create a new user',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = getToolDescriptionForNode(mockNode, mockNodeType);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('Create a new user in Test Node Type');
|
||||
});
|
||||
|
||||
test('should use generated description when toolDescription is empty', () => {
|
||||
// Arrange
|
||||
mockNode.parameters = {
|
||||
descriptionType: 'manual',
|
||||
resource: 'user',
|
||||
operation: 'create',
|
||||
toolDescription: '',
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = getToolDescriptionForNode(mockNode, mockNodeType);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('create user in Test Node Type');
|
||||
});
|
||||
|
||||
test('should use generated description when toolDescription is only whitespace', () => {
|
||||
// Arrange
|
||||
mockNode.parameters = {
|
||||
descriptionType: 'manual',
|
||||
resource: 'user',
|
||||
operation: 'create',
|
||||
toolDescription: ' ',
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = getToolDescriptionForNode(mockNode, mockNodeType);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('create user in Test Node Type');
|
||||
});
|
||||
|
||||
test('should use custom toolDescription when it exists', () => {
|
||||
// Arrange
|
||||
mockNode.parameters = {
|
||||
descriptionType: 'manual',
|
||||
resource: 'user',
|
||||
operation: 'create',
|
||||
toolDescription: 'My custom description',
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = getToolDescriptionForNode(mockNode, mockNodeType);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('My custom description');
|
||||
});
|
||||
|
||||
test('should fall back to node type description when toolDescription is undefined', () => {
|
||||
// Arrange
|
||||
mockNode.parameters = {
|
||||
descriptionType: 'manual',
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = getToolDescriptionForNode(mockNode, mockNodeType);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('This is the default node description');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user