Files
n8n-enterprise-unlocked/packages/editor-ui/src/utils/fromAIOverrideUtils.test.ts

168 lines
4.9 KiB
TypeScript

import type { FromAIOverride, OverrideContext } from './fromAIOverrideUtils';
import {
buildValueFromOverride,
fromAIExtraProps,
isFromAIOverrideValue,
makeOverrideValue,
parseOverrides,
} from './fromAIOverrideUtils';
import type { INodeTypeDescription } from 'n8n-workflow';
const DISPLAY_NAME = 'aDisplayName';
const PARAMETER_NAME = 'aName';
const makeContext = (value: string, path?: string): OverrideContext => ({
parameter: {
name: PARAMETER_NAME,
displayName: DISPLAY_NAME,
type: 'string',
},
value,
path: path ?? `parameters.${PARAMETER_NAME}`,
});
const MOCK_NODE_TYPE_MIXIN = {
version: 0,
defaults: {},
inputs: [],
outputs: [],
properties: [],
displayName: '',
group: [],
description: '',
};
const AI_NODE_TYPE: INodeTypeDescription = {
name: 'AN_AI_NODE_TYPE',
codex: {
categories: ['AI'],
subcategories: {
AI: ['Tools'],
},
},
...MOCK_NODE_TYPE_MIXIN,
};
const AI_DENYLIST_NODE_TYPE: INodeTypeDescription = {
name: 'toolCode',
codex: {
categories: ['AI'],
subcategories: {
AI: ['Tools'],
},
},
...MOCK_NODE_TYPE_MIXIN,
};
const NON_AI_NODE_TYPE: INodeTypeDescription = {
name: 'AN_NOT_AI_NODE_TYPE',
...MOCK_NODE_TYPE_MIXIN,
};
describe('makeOverrideValue', () => {
test.each<[string, ...Parameters<typeof makeOverrideValue>]>([
['null nodeType', makeContext(''), null],
['non-ai node type', makeContext(''), NON_AI_NODE_TYPE],
['ai node type on denylist', makeContext(''), AI_DENYLIST_NODE_TYPE],
])('should not create an override for %s', (_name, context, nodeType) => {
expect(makeOverrideValue(context, nodeType)).toBeNull();
});
it('should create an fromAI override', () => {
const result = makeOverrideValue(
makeContext(`={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('${DISPLAY_NAME}') }}`),
AI_NODE_TYPE,
);
expect(result).not.toBeNull();
expect(result?.type).toEqual('fromAI');
});
it('parses existing fromAI overrides', () => {
const description = 'a description';
const result = makeOverrideValue(
makeContext(
`={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('${DISPLAY_NAME}', \`${description}\`) }}`,
),
AI_NODE_TYPE,
);
expect(result).toBeDefined();
expect(result?.extraPropValues.description).toEqual(description);
});
it('parses an existing fromAI override with default values without adding extraPropValue entry', () => {
const result = makeOverrideValue(
makeContext("={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('aName', ``) }}"),
AI_NODE_TYPE,
);
expect(result).toBeDefined();
expect(result?.extraPropValues.description).not.toBeDefined();
});
});
describe('FromAiOverride', () => {
it('correctly identifies override values', () => {
expect(isFromAIOverrideValue('={{ $fromAI() }}')).toBe(false);
expect(isFromAIOverrideValue('={{ /*n8n-auto-generated-fromAI-override*/ $fromAI() }}')).toBe(
true,
);
});
it('should parseOverrides as expected', () => {
expect(parseOverrides("={{ $fromAI('aKey' }}")).toBeNull();
expect(parseOverrides("={{ $fromAI('aKey') }}")).toEqual({
description: undefined,
});
expect(parseOverrides("={{ $fromAI('aKey', `a description`) }}")).toEqual({
description: 'a description',
});
expect(parseOverrides("={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('aKey') }}")).toEqual(
{ description: undefined },
);
expect(
parseOverrides(
"={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('aKey', `a description`) }}",
),
).toEqual({
description: 'a description',
});
});
test.each<[string, string, string]>([
['none', '$fromAI("a", `b`)', 'b'],
['a simple case of', '$fromAI("a", `\\``)', '`'],
// this is a bug in the current implementation
// see related comments in the main file
// We try to use different quote characters where possible
['a complex case of', '$fromAI("a", `a \\` \\\\\\``)', 'a ` `'],
])('should handle %s backtick escaping ', (_name, value, expected) => {
expect(parseOverrides(value)).toEqual({ description: expected });
});
it('should build a value from an override and carry over modification', () => {
const override: FromAIOverride = {
type: 'fromAI',
extraProps: fromAIExtraProps,
extraPropValues: {},
};
expect(buildValueFromOverride(override, makeContext(''), true)).toEqual(
`={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('${DISPLAY_NAME}', \`\`, 'string') }}`,
);
expect(buildValueFromOverride(override, makeContext(''), false)).toEqual(
`={{ $fromAI('${DISPLAY_NAME}', \`\`, 'string') }}`,
);
const description = 'a description';
override.extraPropValues.description = description;
expect(buildValueFromOverride(override, makeContext(''), true)).toEqual(
`={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('${DISPLAY_NAME}', \`${description}\`, 'string') }}`,
);
expect(buildValueFromOverride(override, makeContext(''), false)).toEqual(
`={{ $fromAI('${DISPLAY_NAME}', \`${description}\`, 'string') }}`,
);
});
});