diff --git a/packages/plugins/@nocobase/plugin-ai/src/client-v2/__tests__/workflow-ai-employee-fieldset.test.tsx b/packages/plugins/@nocobase/plugin-ai/src/client-v2/__tests__/workflow-ai-employee-fieldset.test.tsx index e6261644597..c5cc21d95b7 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client-v2/__tests__/workflow-ai-employee-fieldset.test.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client-v2/__tests__/workflow-ai-employee-fieldset.test.tsx @@ -17,6 +17,11 @@ import { Assignees } from '../workflow/nodes/employee/components/Assignees'; import { FeedbackSettings } from '../workflow/nodes/employee/components/FeedbackSettings'; import { SkillSettings } from '../workflow/nodes/employee/components/SkillSettings'; import { StructuredOutput } from '../workflow/nodes/employee/components/StructuredOutput'; +import { UserInput } from '../workflow/nodes/employee/components/UserInput'; + +const { useWorkflowVariableOptions } = vi.hoisted(() => ({ + useWorkflowVariableOptions: vi.fn((_options?: { types?: Array<(field: unknown) => boolean> }) => []), +})); vi.mock('../locale', () => ({ NAMESPACE: '@nocobase/plugin-ai', @@ -26,8 +31,25 @@ vi.mock('../locale', () => ({ })); vi.mock('@nocobase/flow-engine', () => ({ - FlowContextSelector: ({ onChange }: { onChange?: (value: string) => void }) => ( - ), @@ -86,7 +108,6 @@ vi.mock('@nocobase/plugin-workflow/client-v2', () => { onChange={(event) => onChange?.(JSON.parse(event.target.value) as Record)} /> ); - const useWorkflowVariableOptions = () => []; class Instruction {} return { @@ -170,6 +191,24 @@ describe('AI employee workflow fieldset', () => { await expect(instruction.FieldsetLoader()).resolves.toEqual({ default: AIEmployeeFieldset }); }); + it('accepts context user variables without selecting parent variable categories', () => { + const onChange = vi.fn(); + render(); + + const selector = screen.getByRole('button', { name: 'workflow-variable-selector' }); + expect(selector).toHaveAttribute('data-value', '{{$context.data}}'); + expect(selector).toHaveAttribute('data-only-leaf-selectable', 'true'); + expect(selector).toHaveAttribute('data-dropdown-footer', 'none'); + + const options = useWorkflowVariableOptions.mock.calls.at(-1)?.[0]; + const [isUserKeyField] = options?.types ?? []; + expect(isUserKeyField({ type: 'context', target: 'users' })).toBe(true); + expect(isUserKeyField({ type: 'context', target: 'posts' })).toBe(false); + + fireEvent.click(selector); + expect(onChange).toHaveBeenCalledWith('{{$context.user.id}}'); + }); + it('keeps structured output and normalized approval mode under config when submitted', async () => { const submitted: FormValues[] = []; const schema = { diff --git a/packages/plugins/@nocobase/plugin-ai/src/client-v2/workflow/nodes/employee/components/UserInput.tsx b/packages/plugins/@nocobase/plugin-ai/src/client-v2/workflow/nodes/employee/components/UserInput.tsx index 708a2a94412..083bf2f77c2 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client-v2/workflow/nodes/employee/components/UserInput.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client-v2/workflow/nodes/employee/components/UserInput.tsx @@ -22,6 +22,7 @@ type CollectionFieldLike = { isForeignKey?: boolean; name?: string; target?: string; + type?: string; }; type UserInputProps = { @@ -43,7 +44,7 @@ function isWorkflowVariable(value?: UserInputValue) { } function isUserKeyField(field: CollectionFieldLike) { - if (field.isForeignKey) { + if (field.isForeignKey || field.type === 'context') { return field.target === 'users'; } return field.collectionName === 'users' && field.name === 'id';