feat: 优化工作流参数编辑与告警交互
- 新增 ParamTokenEditor,支持参数选择插入、token 高亮、整段删除与光标避让 - 参数候选改为动态监测,未映射参数可选择并在下拉与输入框顶部告警 - 接入知识库/搜索引擎/LLM/动态代码/HTTP Body 及 SQL、查询数据自定义节点 - 优化 Http 节点布局并补充参数解析工具与单测
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
flattenParameterCandidates,
|
||||
findBackspaceTokenRange,
|
||||
findTokenRangeAtCursor,
|
||||
flattenParameterNames,
|
||||
insertTextAtCursor,
|
||||
parseTokenParts,
|
||||
splitTokenDisplay
|
||||
} from './paramToken';
|
||||
|
||||
describe('paramToken utils', () => {
|
||||
it('should flatten parameter names with nested paths', () => {
|
||||
const result = flattenParameterNames([
|
||||
{
|
||||
name: 'input'
|
||||
},
|
||||
{
|
||||
name: 'documents',
|
||||
children: [
|
||||
{
|
||||
name: 'title'
|
||||
},
|
||||
{
|
||||
name: 'meta',
|
||||
children: [
|
||||
{
|
||||
name: 'author'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
expect(result).toEqual([
|
||||
'input',
|
||||
'documents',
|
||||
'documents.title',
|
||||
'documents.meta',
|
||||
'documents.meta.author'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should keep unresolved candidates in parameter list', () => {
|
||||
const result = flattenParameterCandidates([
|
||||
{
|
||||
name: 'input',
|
||||
refType: 'ref',
|
||||
ref: ''
|
||||
},
|
||||
{
|
||||
name: 'docs',
|
||||
refType: 'ref',
|
||||
ref: 'documents'
|
||||
},
|
||||
{
|
||||
name: 'runtimeInput',
|
||||
refType: 'input'
|
||||
}
|
||||
]);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
name: 'input',
|
||||
resolved: false
|
||||
},
|
||||
{
|
||||
name: 'docs',
|
||||
resolved: true
|
||||
},
|
||||
{
|
||||
name: 'runtimeInput',
|
||||
resolved: true
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should insert token text in the middle by cursor range', () => {
|
||||
const result = insertTextAtCursor('hello world', '{{input}}', 6, 11);
|
||||
expect(result).toEqual({
|
||||
value: 'hello {{input}}',
|
||||
cursor: 15
|
||||
});
|
||||
});
|
||||
|
||||
it('should append token text when cursor info is missing', () => {
|
||||
const result = insertTextAtCursor('hello', '{{name}}');
|
||||
expect(result).toEqual({
|
||||
value: 'hello{{name}}',
|
||||
cursor: 13
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse token parts and mark valid tokens', () => {
|
||||
const parts = parseTokenParts(
|
||||
'你好 {{ user.name }} 与 {{unknown}}',
|
||||
['user.name', 'docs']
|
||||
);
|
||||
|
||||
expect(parts).toEqual([
|
||||
{
|
||||
type: 'text',
|
||||
text: '你好 '
|
||||
},
|
||||
{
|
||||
type: 'token',
|
||||
text: '{{ user.name }}',
|
||||
key: 'user.name',
|
||||
valid: true
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: ' 与 '
|
||||
},
|
||||
{
|
||||
type: 'token',
|
||||
text: '{{unknown}}',
|
||||
key: 'unknown',
|
||||
valid: false
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should keep plain text when token syntax is invalid', () => {
|
||||
const parts = parseTokenParts('abc {{}} def', ['a']);
|
||||
expect(parts).toEqual([
|
||||
{
|
||||
type: 'text',
|
||||
text: 'abc {{}} def'
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should split token display and hide braces text', () => {
|
||||
const result = splitTokenDisplay('{{ user.name }}', 'user.name');
|
||||
expect(result).toEqual({
|
||||
hiddenPrefix: '{{ ',
|
||||
visibleText: 'user.name',
|
||||
hiddenSuffix: ' }}'
|
||||
});
|
||||
});
|
||||
|
||||
it('should find full token range for backspace delete', () => {
|
||||
const content = 'hello {{input}} world';
|
||||
const tokenEndCursor = 'hello {{input}}'.length;
|
||||
const range = findBackspaceTokenRange(content, tokenEndCursor);
|
||||
|
||||
expect(range).toEqual({
|
||||
start: 6,
|
||||
end: 15,
|
||||
text: '{{input}}',
|
||||
key: 'input'
|
||||
});
|
||||
});
|
||||
|
||||
it('should support boundary match for arrow skip behavior', () => {
|
||||
const content = 'x{{docs}}y';
|
||||
const tokenStart = 1;
|
||||
const tokenEnd = 9;
|
||||
|
||||
const rightBoundary = findTokenRangeAtCursor(content, tokenStart, {
|
||||
includeStart: true,
|
||||
includeEnd: false
|
||||
});
|
||||
const leftBoundary = findTokenRangeAtCursor(content, tokenEnd, {
|
||||
includeStart: false,
|
||||
includeEnd: true
|
||||
});
|
||||
|
||||
expect(rightBoundary?.key).toBe('docs');
|
||||
expect(leftBoundary?.key).toBe('docs');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user